<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="哈夫曼（Huffman Encoding）压缩算法-Python实现, python,machine learning,deep learning,html,css,c,c++,cpp,cmake,ros,linux,ubuntu">
    <meta name="description" content="本文主要提供了Python版本的哈夫曼压缩算法实现，并在此基础上提供了命令行和基于Qt的GUI用户界面（User Interface）">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="referrer" content="no-referrer-when-downgrade">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>哈夫曼（Huffman Encoding）压缩算法-Python实现 | JackWang&#39;s Blog</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery-3.6.0.min.js"></script>

<meta name="generator" content="Hexo 5.4.2">
<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css">
<link rel="stylesheet" href="/css/prism-line-numbers.css" type="text/css"></head>



   <style>
    body{
       background-image: url(https://cdn.jsdelivr.net/gh/Tokisaki-Galaxy/res/site/medias/background.jpg);
       background-repeat:no-repeat;
       background-size: 100% 100%;
       background-attachment:fixed;
    }
</style>



<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">JackWang&#39;s Blog</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="" class="waves-effect waves-light">

      
      <i class="fas fa-book-reader" style="zoom: 0.6;"></i>
      
      <span>博客</span>
      <i class="fas fa-chevron-down" aria-hidden="true" style="zoom: 0.6;"></i>
    </a>
    <ul class="sub-nav menus_item_child ">
      
      <li>
        <a href="/tags">
          
          <i class="fas fa-tags" style="margin-top: -20px; zoom: 0.6;"></i>
          
	  <span>按标签归类文章</span>
        </a>
      </li>
      
      <li>
        <a href="/categories">
          
          <i class="fas fa-bookmark" style="margin-top: -20px; zoom: 0.6;"></i>
          
	  <span>按目录归类文章</span>
        </a>
      </li>
      
      <li>
        <a href="/archives">
          
          <i class="fas fa-archive" style="margin-top: -20px; zoom: 0.6;"></i>
          
	  <span>按日期分类文章</span>
        </a>
      </li>
      
    </ul>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>



<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">JackWang&#39;s Blog</div>
        <div class="logo-desc">
            
            JackWang的个人博客
            
        </div>
    </div>

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="javascript:;">
			
				<i class="fa-fw fas fa-book-reader"></i>
			
			博客
			<span class="m-icon"><i class="fas fa-chevron-right"></i></span>
		</a>
            <ul  style="background:  ;" >
              
                <li>

                  <a href="/tags " style="margin-left:75px">
				  
				   <i class="fa fas fa-tags" style="position: absolute;left:50px" ></i>
			      
                              <span>按标签归类文章</    span>

                  </a>
                </li>
              
                <li>

                  <a href="/categories " style="margin-left:75px">
				  
				   <i class="fa fas fa-bookmark" style="position: absolute;left:50px" ></i>
			      
                              <span>按目录归类文章</    span>

                  </a>
                </li>
              
                <li>

                  <a href="/archives " style="margin-left:75px">
				  
				   <i class="fa fas fa-archive" style="position: absolute;left:50px" ></i>
			      
                              <span>按日期分类文章</    span>

                  </a>
                </li>
              
            </ul>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    
<script src="/libs/cryptojs/crypto-js.min.js"></script>
<script>
    (function() {
        let pwd = '';
        if (pwd && pwd.length > 0) {
            if (pwd !== CryptoJS.SHA256(prompt('抱歉，这篇文章并不想让所有人都看到，请输入授权密码观看')).toString(CryptoJS.enc.Hex)) {
                alert('密码错误，将返回主页！');
                location.href = '/';
            }
        }
    })();
</script>




<div class="bg-cover pd-header post-cover" style="background-image: url('https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211222111040507.png')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">哈夫曼（Huffman Encoding）压缩算法-Python实现</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/DataStructure/">
                                <span class="chip bg-color">DataStructure</span>
                            </a>
                        
                            <a href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">
                                <span class="chip bg-color">数据结构</span>
                            </a>
                        
                            <a href="/tags/Python/">
                                <span class="chip bg-color">Python</span>
                            </a>
                        
                            <a href="/tags/GUI/">
                                <span class="chip bg-color">GUI</span>
                            </a>
                        
                            <a href="/tags/PyQt/">
                                <span class="chip bg-color">PyQt</span>
                            </a>
                        
                            <a href="/tags/HuffmanEncode/">
                                <span class="chip bg-color">HuffmanEncode</span>
                            </a>
                        
                            <a href="/tags/HuffmanAlgorithm/">
                                <span class="chip bg-color">HuffmanAlgorithm</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/" class="post-category">
                                数据结构与算法
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2021-12-21
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>更新日期:&nbsp;&nbsp;
                    2023-06-01
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    9.3k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    43 分
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        

        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <blockquote>
<p>本文主要提供了Python版本的哈夫曼压缩算法实现，并在此基础上提供了命令行和基于Qt的GUI用户界面（User Interface）</p>
</blockquote>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/all.gif" alt="完整效果"></p>
<h1 id="哈夫曼（Huffman-Encoding）压缩算法-Python实现"><a href="#哈夫曼（Huffman-Encoding）压缩算法-Python实现" class="headerlink" title="哈夫曼（Huffman Encoding）压缩算法-Python实现"></a>哈夫曼（Huffman Encoding）压缩算法-Python实现</h1><p>哈夫曼编码作为计算机届非常底层的算法，不少领域都会出现该算法的身影，例如在MPEG图片压缩算法中等等。因此掌握哈夫曼算法以及相关的哈弗曼编码、哈弗曼树实现还是比较必要的。</p>
<h2 id="0-前言"><a href="#0-前言" class="headerlink" title="0. 前言"></a>0. 前言</h2><p>我目前是一个大二的计算机系学生，最近数据结构课程的配套实验要求学生从十四个题中选择四个来做。刚好最近学习了PyQt的相关内容，于是本着学习的目的把我完成的成果记录下来，希望我的这篇博客能够帮助大家学习与理解。</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221220005902.png" alt="数据结构算法实验"></p>
<h2 id="1-编码"><a href="#1-编码" class="headerlink" title="1. 编码"></a>1. 编码</h2><p>所有的文件，包括MP3音频、MP4视频、PDF文档、乃至于可执行文件（Windows上的.EXE、Linux中的.O等ELF格式文件），对于计算机来说都是二进制数据流，即由0101的比特构成的流（Stream）。</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221222101136.png" alt="计算机底层以二进制形式存储文件"></p>
<p>然而二进制流对于计算机而言是有意义的，对人而言却没有多大的意义了，因为人看不懂二进制的字节流。于是为了解决这个问题，就出现了编码。所谓<strong>编码即指将信息从一种格式转换为另外一种格式而不改变其内容</strong>，更详细的介绍，参考下面百度百科和维基百科的介绍。</p>
<blockquote>
<p><strong>From Wikipedia</strong></p>
<p>编码是信息从一种形式或格式转换为另一种形式的过程；解码则是编码的逆过程。</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221221212302.png" alt="维基百科上对编码的介绍"></p>
<p><strong>From BaiduBaike</strong>:</p>
<p>编码是信息从一种形式或格式转换为另一种形式的过程，也称为计算机编程语言的代码简称编码。用预先规定的方法将文字、数字或其它对象编成数码，或将信息、数据转换成规定的电脉冲信号。编码在电子计算机、电视、遥控和通讯等方面广泛使用。编码是信息从一种形式或格式转换为另一种形式的过程。解码，是编码的逆过程。</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221221358810.png" alt="百度百科上对编码的介绍"></p>
</blockquote>
<p>因此，编码其实指的就是转变信息的形式而不改变信息的内容。之所以要进行编码与解码，就是因为对于不同的主体而言，不同编码方式的信息的阅读难度和理解难度不同。例如上面的三国演义，对于我们人而言，文字形式的三国演义易于理解而二进制形式的三国演义更利于计算机理解。</p>
<p>换个角度，其实语种之间的转换也是一种编码的转换。例如英语对于中文母语的人就不好理解。而翻译也是把信息从一种语言转换为另一种语言，其形式在变，但本质内涵的信息却没有变。<strong>所以究其根本，编码的目的在于方便信息的理解。</strong></p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221230110176.png" alt="英文版的三国演义"></p>
<p>话说回来，要进行编码，就需要一张编码表。例如学C语言时候的ASCII表。我们通过编码表构建了两种不同编码之间的映射关系，因此单纯的ASCII字符的转换，就是数值的映射关系，从而通过数值-字符的转换，实现了编码与解码。其实从语言的角度来看，语言的编码不仅仅是单词的映射，还涉及语法的转换等等更加复杂的关系。</p>
<p>然而单纯的使用ASCII编码或者UTF8这种编码方式其实虽然很不错，计算机能够理解，但是他们的问题就是太浪费空间了。例如针对ASCII编码，每一个字符需要8个比特位（0-255）。因此哈夫曼编码除了方便计算机理解以外，更大的好处是能够节约空间。</p>
<p>说了这么久，总算是说到了哈夫曼编码，哈夫曼编码其实就是一种编码，和ASCII编码在本质上没有区别的。</p>
<h2 id="2-哈夫曼算法-Huffman-Coding"><a href="#2-哈夫曼算法-Huffman-Coding" class="headerlink" title="2. 哈夫曼算法 / Huffman Coding"></a>2. 哈夫曼算法 / Huffman Coding</h2><p>关于哈夫曼编码和对应的哈夫曼算法就不赘述了，在数据结构的课本上其实讲得非常好也非常的透彻。下面就放一下百度百科和维基百科的介绍</p>
<blockquote>
<p><strong>From Wikipedia</strong>：</p>
<p>霍夫曼编码（英语：Huffman Coding），又译为哈夫曼编码、赫夫曼编码，是一种用于无损数据压缩的熵编码（权编码）算法。由美国计算机科学家大卫·霍夫曼（David Albert Huffman）在1952年发明。</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221231837051.png" alt="维基百科上哈夫曼编码的介绍"></p>
<p><strong>From BaiduBaike</strong>：</p>
<p>哈夫曼编码(Huffman Coding)，又称霍夫曼编码，是一种编码方式，哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法，该方法完全依据字符出现概率来构造异字头的平均长度最短的码字，有时称之为最佳编码，一般就叫做Huffman编码（有时也称为霍夫曼编码）。</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211221231927090.png" alt="百度百科上哈夫曼编码的介绍"></p>
</blockquote>
<h2 id="3-实现思路"><a href="#3-实现思路" class="headerlink" title="3. 实现思路"></a>3. 实现思路</h2><p>针对不同的功能，整个程序的实现，主要分为三个层面：</p>
<ol>
<li><strong>核心层</strong>：负责哈夫曼算法的实现</li>
<li><strong>数据存取层</strong>：负责任意形式数据的读取、保存</li>
<li><strong>用户界面层</strong>：负责提供命令行用户界面以及图形用户接口，从而实现<del>高级感</del>（逼格）</li>
</ol>
<h3 id="A-核心层"><a href="#A-核心层" class="headerlink" title="A. 核心层"></a>A. 核心层</h3><p>核心层的任务其实就是构建哈弗曼树、得到哈夫曼编码即可，主要算法其实在数据结构课本、B站视频、知乎上都有不少介绍。 </p>
<p>然而一个重要的问题就是课本上和B站中的不少介绍视频都是针对字符（字母）进行哈夫曼编码的。我们由于要对任意格式的文件进行编码，因此就要从数据的本源：字节，进行哈夫曼编码。因此我们哈弗曼树的叶子节点实际上是字节。我们以8位二进制作为一个单元（Symbol）。然后针对这个Symbol进行编码。因此我们编码的对象就从26个字母变成了256种八位二进制数字。换而言之我们是对0~255这256个数字进行编码。</p>
<p>然后根据得到的哈夫曼编码将原字节数据一个字节（八位比特）进行编码、转换为哈夫曼编码即可。</p>
<h3 id="B-数据存取层"><a href="#B-数据存取层" class="headerlink" title="B. 数据存取层"></a>B. 数据存取层</h3><p>在数据存取层，我们假设现在已经对原数据（raw data）构建出了哈弗曼树并且得到了原数据的哈弗曼编码，而且根据这些哈夫曼编码得到了原数据的哈夫曼编码字节流。我们在数据存取层需要做的就是</p>
<ol>
<li>保存转换后的新的哈夫曼编码的字节流</li>
<li>读取原始数据的二进制字节流</li>
<li>保存哈夫曼编码表</li>
</ol>
<p>读取原始数据的二进制字节流其实问题不大，Python中以rb（read byte）模式打开文件即可。而哈夫曼编码表的保存则可以利用Python的Pickle模块将Python中的对象保存到磁盘/从磁盘中加载（我们以字典/映射的形式保存编码表）</p>
<p>因此数据存取层的关键就在于如何保存编码后的哈夫曼编码字节流？这个问题看似简单，其实有一个问题，就是（二进制）数据在计算机外存（固态、硬盘）中的保存都是整字节整字节的保存的，读取也是整字节整字节读取的。而我们对原文件的二进制流进行哈夫曼编码后实际上其长度是不定的，不一定是整字节的。</p>
<p>因此保存的时候虽然我们可以强行以整字节整字节的保存编码后的二进制流，但是在最后一位由于不一定是一个整字节，因此实际上存在问题。为了处理这个问题，我们设定一个特殊的字符EOF，由于8位二进制能表示256个数字（Symbol），而这些数字都有可能出现在原字节流中，因此我们是不能直接选取一个数字来表示文件结尾的。为此，在Python中，通过一个特定的类来实现文件结尾的表示，即我们自定义的EOF。在大文件（几MB的文件）中，一共有1024*1024个字节，因此从0~255这256个数字出现的次数一定是远远大于1的，而EOF的出现次数永远为1，因此必然会拥有最长的编码。</p>
<p>在我们转换完所有的原数据之后，我们把EOF添加到末尾去，并且如果最后不满足8位的话在最后补0。而在读取的时候，我们只需要获取最长长度的编码的长度，然后对最后一个字节做截取即可得到正确的完整的带有EOF的编码的编码后的原数据的哈夫曼编码字节流。</p>
<h3 id="C-用户界面层"><a href="#C-用户界面层" class="headerlink" title="C. 用户界面层"></a>C. 用户界面层</h3><p>在用户界面层，不关心具体的哈夫曼编码的实现、数据的保存方式等这些具体的细节。相比，用户界面层仅关注如何提供一个用户友好的用户界面。例如在终端（Terminal）提供选项，让用户制定压缩、解压的文件。还会检查用户指定的文件是否存在、查找用户需要解码的文件对应的的哈弗曼编码表的文件位置。</p>
<p>当然，GUI使用会让程序显得非常高级，因此GUI部分是必须的。而完成GUI程序，则借助Qt来完成，即使用Qt的Python Binding。</p>
<p>当然，为了便于写代码管理与开发，需要注意GUI的代码要与完成功能的代码在逻辑和形式上分离，因此采取前面所说的分层的思想。</p>
<p>因此将GUI作为用户界面层的一个组件（另一个组件为终端命令行接口），调用由数据存取层提供的接口函数完成功能。</p>
<p>在GUI内部，进行在GUI窗口上发生的事件的监听，以及轮转调度、回调函数调用等等功能。</p>
<h2 id="4-具体代码"><a href="#4-具体代码" class="headerlink" title="4. 具体代码"></a>4. 具体代码</h2><p>本部分根据上面的讲解，对照代码进行详细的介绍。</p>
<p>项目结构如下</p>
<pre class="line-numbers language-bash"><code class="language-bash"><span class="token punctuation">(</span>base<span class="token punctuation">)</span> jack@Alienware:~/projects/data_structure/11_huffman$ tree -L 2
<span class="token keyword">.</span>
├── ErrorHuffman.py
├── huff.py
├── resources
│   ├── 三国演绎.txt
│   ├── D_Star_illustration.pptx
│   ├── ICRA2022.pdf
│   ├── romance_of_three_kingdoms.rar
│   ├── Song.mp3
│   ├── str.txt
│   └── test.mp4
├── ui
│   └── main.ui
└── Ui_main.py

2 directories, 11 files
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>其中：</p>
<ul>
<li>resource为项目资源文件夹，主要存放稍后用于验证压缩的资源，包括MP4、txt、MP3、rar、PDF等多种格式文件</li>
<li>ui为程序图形界面配置文件，负责设定界面大小、按钮位置等配置参数。而UI_main为具体的按钮等控件的实现代码</li>
<li>huff.py为最终的程序，内含所有完整代码；errHuff.py为先前写错的代码，问题出在了哈夫曼建树的时候得到的不是范式哈弗曼树，因此该程序遍历哈弗树得到哈夫曼编码的时候有问题，最后压缩出来的文件比源文件大了好几倍（反向压缩。。。应了刘某的反向抽烟XXX<span class="github-emoji"><span>😂</span><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f602.png?v8" aria-hidden="true" onerror="this.parent.classList.add('github-emoji-fallback')"></span>）</li>
</ul>
<h3 id="1-导入库"><a href="#1-导入库" class="headerlink" title="1. 导入库"></a>1. 导入库</h3><p>主要导入的库为命令行参数获取库argparse、Python对象序列化库Pickle、路径处理库pathlib、堆管理库heapq、GUI库PyQt以及为了更好的显示得到的哈夫曼编码表的pandas库与命令行彩色输出的colorama库。</p>
<p>最后定义了文件的私有变量设定作者和日期，以及将可能存在源代码的文件加入文件搜索路径（类似于Linux环境变量LD_LIBRARY_PATH）</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">import</span> sys
<span class="token keyword">import</span> pickle
<span class="token keyword">import</span> argparse
<span class="token keyword">import</span> itertools
<span class="token keyword">import</span> collections
<span class="token keyword">from</span> pathlib <span class="token keyword">import</span> Path
<span class="token keyword">from</span> heapq <span class="token keyword">import</span> heapify<span class="token punctuation">,</span> heappush<span class="token punctuation">,</span> heappop
<span class="token keyword">from</span> typing <span class="token keyword">import</span> Union<span class="token punctuation">,</span> Any<span class="token punctuation">,</span> Dict<span class="token punctuation">,</span> List<span class="token punctuation">,</span> Tuple
<span class="token keyword">from</span> PyQt5<span class="token punctuation">.</span>QtCore <span class="token keyword">import</span> QDir

<span class="token keyword">import</span> pandas
<span class="token keyword">import</span> pandas <span class="token keyword">as</span> pd
<span class="token keyword">from</span> colorama <span class="token keyword">import</span> Fore<span class="token punctuation">,</span> Style
<span class="token keyword">from</span> PyQt5<span class="token punctuation">.</span>QtWidgets <span class="token keyword">import</span> <span class="token operator">*</span>

__author__ <span class="token operator">=</span> <span class="token string">"Jack Wang"</span>
__date__ <span class="token operator">=</span> <span class="token string">"2021/12/19"</span>
sys<span class="token punctuation">.</span>path<span class="token punctuation">.</span>append<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>__file__<span class="token punctuation">)</span><span class="token punctuation">.</span>resolve<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>parent<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span><span class="token string">"./ui"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__str__<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="2-定义EOF符号"><a href="#2-定义EOF符号" class="headerlink" title="2. 定义EOF符号"></a>2. 定义EOF符号</h3><p>EOF符号由于和0-255个数字一样将在构建树的进行比较，因此需要重载如等于（__eq__）、大于(__gt__)、小于(__lt__)等运算符。在Python中重载运算符以魔术方法（double underline）实现。最后由于在序列化以及字典中保存符号-哈夫曼编码键值对的时候，符号作为字典的键需要时可哈希的对象（Hashable），因此重写__hash__方法，以实现作为字典的键。</p>
<p>注意，上面说过了EOF需要保证编码是最长的，因此当合别人比大的时候总是False，比小的时候总是True，并且只会等于同样是EOF的对象</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">_EOFSymbol</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        _EOFSymbol 是内置的文件结尾的类，用于在编码的时候标志文件的结尾。
        由于在进行哈夫曼树的构建的时候需要选出来权最小的节点，因此需要重写比较的魔术方法。
        默认文档结尾是频率最少的字符，因此和任何数字比较
    """</span>

    <span class="token keyword">def</span> <span class="token function">__repr__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token string">"_EOF"</span>

    <span class="token keyword">def</span> <span class="token function">__lt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token boolean">True</span>

    <span class="token keyword">def</span> <span class="token function">__gt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token boolean">False</span>

    <span class="token keyword">def</span> <span class="token function">__eq__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>__class__ <span class="token operator">==</span> other<span class="token punctuation">.</span>__class__

    <span class="token keyword">def</span> <span class="token function">__hash__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> hash<span class="token punctuation">(</span>self<span class="token punctuation">.</span>__class__<span class="token punctuation">)</span>

_EOF <span class="token operator">=</span> _EOFSymbol<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="3-哈夫曼编码"><a href="#3-哈夫曼编码" class="headerlink" title="3. 哈夫曼编码"></a>3. 哈夫曼编码</h3><p>采用工厂模式，实现哈夫曼编码与解码的核心对象为_HuffmanCoder，与用于构建_HuffmanCoder的HuffmanFactory类。</p>
<p>_HuffmanCoder类负责：</p>
<ul>
<li>输出/打印哈夫曼编码表：get_code_table方法、print_code_table方法</li>
<li>编码、解码字节流序列：_encode_streaming方法、_decode_streaming方法</li>
<li>为用户界面层提供统一接口：encode方法、decode方法</li>
<li>保存、加载哈夫曼编码表：save方法、load方法</li>
</ul>
<p>其初始化参数负责接受已经获得的编码表和符号的拼接方式以及eof符号定义</p>
<p>此外save方法通过Python内建的字典将类的元数据保存到字典中，并利用Python的Pickle模块序列化到磁盘上。而load方法则通过类方法，在通过Pickle加载通过save方法保存到磁盘上的数据后显示调用类的初始化方法以实现_HuffmanCoder类的重构。</p>
<p>此外，在保存的时候需要确保保存的文件的路径存在，因此编写ensure_dir函数，而_guess_concat_function则是为支持多种类型的Symbol（字符串、二进制流（串））在编码、解码时候拼接所写的函数，即根据输入的类型来获得拼接方法的函数。</p>
<p>HuffmanCoderFactory类负责：</p>
<ul>
<li>获得哈夫曼编码表：from_frequencies方法</li>
<li>创建_HuffmanCoder类：from_sequence方法、from_frequencies方法。</li>
</ul>
<p>其中from_frequencies接受符号-频数键值对并构建哈弗曼树、获得哈夫曼编码表；from_sequence负责统计序列中的符号的频数，由于需要对任意的数据进行压缩，因此符号即指一个字节。</p>
<p>在进行构建的时候，利用优先队列，以出现频率作为优先级进行排列，每次pop都会获得优先队列顶的元素，即出现频率最小的元素。</p>
<p>而队列中的每个元素，则是一个元组，元祖的第一个元素为权值（频率），<strong>第二个元素为以该节点为祖宗的叶子节点的二进制编码（以数字形式表现）和对应的比特位数（所处的哈弗曼树的层数/深度）对</strong>。使用数字是由于位移操作方便构建哈弗曼编码而记录比特位数则是为了记录开头为0的哈夫曼编码的总位数。</p>
<p>具体流程为只要队列（森林）中的节点多于1个，则首先弹出权值较小的子树a与权值较大的子树b，将子树a与b的频率相加作为新的子树的频率，而后将子树a的所有的叶子节点的深度+1（比特位数），然后在原二进制编码前加上0（维持不变）。而对子树b，将其所有的叶子节点的二进制编码前加上1（将1左移原有比特位数（原有深度），然后加上当前二进制编码），最后将其深度（比特位数）加1。即使用递归的方式完成哈弗曼树的构建与哈夫曼编码的同步获取。</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">_guess_concat_function</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        To support multiple types of input, e.g., str, bytes, list, there must be a
        function that return the needed concat function
    Returns:
        pass
    """</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>type<span class="token punctuation">(</span>u<span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span> u<span class="token string">""</span><span class="token punctuation">.</span>join<span class="token punctuation">,</span> type<span class="token punctuation">(</span>b<span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span> bytes<span class="token punctuation">}</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span>type<span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">,</span> list<span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">ensure_dir</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Path<span class="token punctuation">:</span>
    path<span class="token punctuation">:</span> Path <span class="token operator">=</span> path <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>path<span class="token punctuation">,</span> Path<span class="token punctuation">)</span> <span class="token keyword">else</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> path<span class="token punctuation">.</span>exists<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        path<span class="token punctuation">.</span>mkdir<span class="token punctuation">(</span>parents<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    <span class="token keyword">assert</span> path<span class="token punctuation">.</span>is_dir<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> path

<span class="token keyword">class</span> <span class="token class-name">_HuffmanCoder</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        _HuffmanCoder, which provide encode, decode, serialize, deserialize, save_code, load_code functionalities.
    Args:

    """</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> code_table<span class="token punctuation">:</span> Dict<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> concat<span class="token operator">=</span>list<span class="token punctuation">,</span> check<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">,</span> eof<span class="token operator">=</span>_EOF<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            initialize method of _HuffmanCoder with given code table
        Args:
            code_table (Dict[Any, Tuple[int, int]]): code table of all symbols in the encoding sequence, create automatically by
                HuffmanCoderFactory, items are Tuple[Symbol, [depth, bit]]
            concat: concat method of symbols, will be automatically determined in HuffmanCoderFactory
            check: valid code table's format
            eof: eof symbol, leave it alone
        """</span>
        self<span class="token punctuation">.</span>_table <span class="token operator">=</span> code_table
        self<span class="token punctuation">.</span>_concat <span class="token operator">=</span> concat
        self<span class="token punctuation">.</span>_eof <span class="token operator">=</span> eof
        <span class="token keyword">if</span> check<span class="token punctuation">:</span>
            <span class="token keyword">assert</span> isinstance<span class="token punctuation">(</span>self<span class="token punctuation">.</span>_table<span class="token punctuation">,</span> dict<span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}Code table need to be a dict!{Style.RESET_ALL}"</span>
            <span class="token keyword">assert</span> all<span class="token punctuation">(</span>
                isinstance<span class="token punctuation">(</span>b<span class="token punctuation">,</span> int<span class="token punctuation">)</span> <span class="token operator">and</span> b <span class="token operator">>=</span> <span class="token number">1</span> <span class="token operator">and</span> isinstance<span class="token punctuation">(</span>v<span class="token punctuation">,</span> int<span class="token punctuation">)</span> <span class="token operator">and</span> v <span class="token operator">>=</span> <span class="token number">0</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">.</span>values<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}Code table internal format Error!{Style.RESET_ALL}"</span>

    <span class="token keyword">def</span> <span class="token function">get_code_table</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Dict<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            get_code_table returns code table of input sequence
        Returns:
            self._table
        """</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>_table

    <span class="token keyword">def</span> <span class="token function">print_code_table</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> pandas<span class="token punctuation">.</span>DataFrame<span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            print_code_table is used to print code table
        Args:
            verbose (bool): if True, then print the table in a pretty format
        Returns:
            pd.DataFrame
        """</span>
        pd<span class="token punctuation">.</span>options<span class="token punctuation">.</span>display<span class="token punctuation">.</span>max_rows <span class="token operator">=</span> None
        columns <span class="token operator">=</span> list<span class="token punctuation">(</span>zip<span class="token punctuation">(</span><span class="token operator">*</span>list<span class="token punctuation">(</span>zip<span class="token punctuation">(</span><span class="token operator">*</span>itertools<span class="token punctuation">.</span>chain<span class="token punctuation">(</span>
            <span class="token punctuation">[</span><span class="token punctuation">(</span><span class="token string">"Symbol"</span><span class="token punctuation">,</span> <span class="token string">"Huff Code"</span><span class="token punctuation">,</span> <span class="token string">"BitSize"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>
                <span class="token punctuation">(</span>repr<span class="token punctuation">(</span>symbol<span class="token punctuation">)</span><span class="token punctuation">,</span> bin<span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">.</span>rjust<span class="token punctuation">(</span>bits<span class="token punctuation">,</span> <span class="token string">"0"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> str<span class="token punctuation">(</span>bits<span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token keyword">for</span> symbol<span class="token punctuation">,</span> <span class="token punctuation">(</span>bits<span class="token punctuation">,</span> val<span class="token punctuation">)</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">)</span>
        <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        df <span class="token operator">=</span> pd<span class="token punctuation">.</span>DataFrame<span class="token punctuation">(</span>columns<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">,</span> columns<span class="token operator">=</span>columns<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> verbose<span class="token punctuation">:</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>df<span class="token punctuation">)</span>
        <span class="token keyword">return</span> df

    <span class="token keyword">def</span> <span class="token function">_encode_streaming</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> raw_sequence<span class="token punctuation">)</span><span class="token punctuation">:</span>
        size <span class="token operator">=</span> <span class="token number">0</span>
        buffer <span class="token operator">=</span> <span class="token number">0</span>
        <span class="token keyword">for</span> s <span class="token keyword">in</span> raw_sequence<span class="token punctuation">:</span>
            bits<span class="token punctuation">,</span> values <span class="token operator">=</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">[</span>s<span class="token punctuation">]</span>
            buffer <span class="token operator">=</span> <span class="token punctuation">(</span>buffer <span class="token operator">&lt;&lt;</span> bits<span class="token punctuation">)</span> <span class="token operator">+</span> values
            size <span class="token operator">+=</span> bits
            <span class="token keyword">while</span> size <span class="token operator">>=</span> <span class="token number">8</span><span class="token punctuation">:</span>
                byte <span class="token operator">=</span> buffer <span class="token operator">>></span> <span class="token punctuation">(</span>size <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span>
                <span class="token keyword">yield</span> byte
                buffer <span class="token operator">=</span> buffer <span class="token operator">-</span> <span class="token punctuation">(</span>byte <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span>size <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                size <span class="token operator">-=</span> <span class="token number">8</span>
        <span class="token keyword">if</span> size <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">:</span>
            bit<span class="token punctuation">,</span> value <span class="token operator">=</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">[</span>self<span class="token punctuation">.</span>_eof<span class="token punctuation">]</span>
            buffer <span class="token operator">=</span> <span class="token punctuation">(</span>buffer <span class="token operator">&lt;&lt;</span> bit<span class="token punctuation">)</span> <span class="token operator">+</span> value
            size <span class="token operator">+=</span> bit
            <span class="token keyword">if</span> size <span class="token operator">></span> <span class="token number">8</span><span class="token punctuation">:</span>
                byte <span class="token operator">=</span> buffer <span class="token operator">>></span> <span class="token punctuation">(</span>size <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                byte <span class="token operator">=</span> buffer <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span><span class="token number">8</span> <span class="token operator">-</span> size<span class="token punctuation">)</span>
            <span class="token keyword">yield</span> byte

    <span class="token keyword">def</span> <span class="token function">_decode_streaming</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> encoded_sequence<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># Reverse lookup table: map (bitsize, value) to symbols</span>
        lookup <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">(</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">:</span> s <span class="token keyword">for</span> s<span class="token punctuation">,</span> <span class="token punctuation">(</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>

        buffer <span class="token operator">=</span> <span class="token number">0</span>
        size <span class="token operator">=</span> <span class="token number">0</span>
        <span class="token keyword">for</span> byte <span class="token keyword">in</span> encoded_sequence<span class="token punctuation">:</span>
            <span class="token keyword">for</span> m <span class="token keyword">in</span> <span class="token punctuation">[</span><span class="token number">128</span><span class="token punctuation">,</span> <span class="token number">64</span><span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
                buffer <span class="token operator">=</span> <span class="token punctuation">(</span>buffer <span class="token operator">&lt;&lt;</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> bool<span class="token punctuation">(</span>byte <span class="token operator">&amp;</span> m<span class="token punctuation">)</span>
                size <span class="token operator">+=</span> <span class="token number">1</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>size<span class="token punctuation">,</span> buffer<span class="token punctuation">)</span> <span class="token keyword">in</span> lookup<span class="token punctuation">:</span>
                    symbol <span class="token operator">=</span> lookup<span class="token punctuation">[</span>size<span class="token punctuation">,</span> buffer<span class="token punctuation">]</span>
                    <span class="token keyword">if</span> symbol <span class="token operator">==</span> self<span class="token punctuation">.</span>_eof<span class="token punctuation">:</span>
                        <span class="token keyword">return</span>
                    <span class="token keyword">yield</span> symbol
                    buffer <span class="token operator">=</span> <span class="token number">0</span>
                    size <span class="token operator">=</span> <span class="token number">0</span>

    <span class="token keyword">def</span> <span class="token function">encode</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> raw_seq<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            encode is used to encode the sequence that build the huffman tree
        Args:
            raw_seq: sequence that build the huffman tree
        Returns:
            bytes
        """</span>
        <span class="token keyword">return</span> bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>_encode_streaming<span class="token punctuation">(</span>raw_seq<span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">decode</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> encoded_seq<span class="token punctuation">,</span> concat<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token punctuation">(</span>concat <span class="token operator">or</span> self<span class="token punctuation">.</span>_concat<span class="token punctuation">)</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>_decode_streaming<span class="token punctuation">(</span>encoded_seq<span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">save</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">,</span> metadata<span class="token punctuation">:</span> Any <span class="token operator">=</span> None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        code_table <span class="token operator">=</span> self<span class="token punctuation">.</span>get_code_table<span class="token punctuation">(</span><span class="token punctuation">)</span>
        data <span class="token operator">=</span> <span class="token punctuation">{</span>
            <span class="token string">"code_table"</span><span class="token punctuation">:</span> code_table<span class="token punctuation">,</span>
            <span class="token string">"type"</span><span class="token punctuation">:</span> type<span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"concat"</span><span class="token punctuation">:</span> self<span class="token punctuation">.</span>_concat<span class="token punctuation">,</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> metadata<span class="token punctuation">:</span>
            data<span class="token punctuation">[</span><span class="token string">'metadata'</span><span class="token punctuation">]</span> <span class="token operator">=</span> metadata
        path <span class="token operator">=</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
        ensure_dir<span class="token punctuation">(</span>path<span class="token punctuation">.</span>parent<span class="token punctuation">)</span>
        <span class="token keyword">with</span> path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">'wb'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
            pickle<span class="token punctuation">.</span>dump<span class="token punctuation">(</span>data<span class="token punctuation">,</span> file<span class="token operator">=</span>f<span class="token punctuation">)</span>

    @staticmethod
    <span class="token keyword">def</span> <span class="token function">load</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token string">'_HuffmanCoder'</span><span class="token punctuation">:</span>
        path <span class="token operator">=</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
        <span class="token keyword">with</span> path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">'rb'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
            data <span class="token operator">=</span> pickle<span class="token punctuation">.</span>load<span class="token punctuation">(</span>f<span class="token punctuation">)</span>
        cls <span class="token operator">=</span> data<span class="token punctuation">[</span><span class="token string">'type'</span><span class="token punctuation">]</span>
        <span class="token keyword">assert</span> issubclass<span class="token punctuation">(</span>cls<span class="token punctuation">,</span> _HuffmanCoder<span class="token punctuation">)</span>
        code_table <span class="token operator">=</span> data<span class="token punctuation">[</span><span class="token string">'code_table'</span><span class="token punctuation">]</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">(</span>code_table<span class="token punctuation">,</span> concat<span class="token operator">=</span>data<span class="token punctuation">[</span><span class="token string">'concat'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>


<span class="token keyword">class</span> <span class="token class-name">HuffmanCoderFactory</span><span class="token punctuation">(</span>_HuffmanCoder<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        HuffmanCodecFactory is the class that create HuffmanCoder from different type of inputs
    Methods:
        from_frequencies: create HuffmanCoder from frequency table
    """</span>

    @classmethod
    <span class="token keyword">def</span> <span class="token function">from_frequencies</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> frequencies<span class="token punctuation">:</span> Dict<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">,</span> concat<span class="token operator">=</span>None<span class="token punctuation">,</span> eof<span class="token operator">=</span>_EOF<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            from_frequencies creates huffman codec by symbol-frequency table/mapping.
        Args:
            frequencies (Dict[Any, int]): Symbols and its frequency, symbols can be str, bytes or int, etc.
            concat (Union[None]): concat method of symbols, will be determined by the function if argument is not
                specified
            eof (_EOFSymbol): leave it alone.
        Returns:
            __HuffmanCoder
        Examples:
            >>> huf_coder = HuffmanCoderFactory.from_frequencies({"a":29, "b":10, "c": 5})
            >>> type(huf_coder)
        """</span>
        concat_function <span class="token operator">=</span> concat <span class="token keyword">if</span> concat <span class="token keyword">is</span> <span class="token operator">not</span> None <span class="token keyword">else</span> _guess_concat_function<span class="token punctuation">(</span>next<span class="token punctuation">(</span>iter<span class="token punctuation">(</span>frequencies<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

        <span class="token comment" spellcheck="true"># build huffman tree node heap</span>
        <span class="token comment" spellcheck="true"># each item: (frequency, [(symbol, (bitsize, value))], value equals which layer of the tree</span>
        heap<span class="token punctuation">:</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>freq<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>symbol<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">for</span> symbol<span class="token punctuation">,</span> freq <span class="token keyword">in</span>
                                                                     frequencies<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
        <span class="token comment" spellcheck="true"># add eof</span>
        <span class="token keyword">if</span> eof <span class="token operator">not</span> <span class="token keyword">in</span> frequencies<span class="token punctuation">:</span>
            heap<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>eof<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

        heapify<span class="token punctuation">(</span>heap<span class="token punctuation">)</span>
        <span class="token keyword">while</span> len<span class="token punctuation">(</span>heap<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">1</span><span class="token punctuation">:</span>
            <span class="token comment" spellcheck="true"># get first 2 min as left and right child tree</span>
            a<span class="token punctuation">:</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> heappop<span class="token punctuation">(</span>heap<span class="token punctuation">)</span>
            b<span class="token punctuation">:</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> heappop<span class="token punctuation">(</span>heap<span class="token punctuation">)</span>
            <span class="token comment" spellcheck="true"># merge child to form parent</span>
            <span class="token comment" spellcheck="true"># parent frequencies adds together, left child add 0 ahead (do nothing) of previous bits</span>
            <span class="token comment" spellcheck="true"># right add 1 ahead of previous bits</span>
            merged <span class="token operator">=</span> <span class="token punctuation">(</span>
                a<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">+</span> b<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
                <span class="token punctuation">[</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">in</span> a<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">&lt;&lt;</span> n<span class="token punctuation">)</span> <span class="token operator">+</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">in</span> b<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
            <span class="token punctuation">)</span>
            heappush<span class="token punctuation">(</span>heap<span class="token punctuation">,</span> merged<span class="token punctuation">)</span>

        <span class="token comment" spellcheck="true"># code table is root</span>
        table <span class="token operator">=</span> dict<span class="token punctuation">(</span>heappop<span class="token punctuation">(</span>heap<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">(</span>table<span class="token punctuation">,</span> concat<span class="token operator">=</span>concat<span class="token punctuation">,</span> check<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">,</span> eof<span class="token operator">=</span>eof<span class="token punctuation">)</span>

    @classmethod
    <span class="token keyword">def</span> <span class="token function">from_sequence</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> sequence<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>List<span class="token punctuation">[</span>Any<span class="token punctuation">]</span><span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">]</span><span class="token punctuation">,</span> str<span class="token punctuation">,</span> bytes<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            from_sequence will build a huffman tree from a sequence of symbol
        Args:
            sequence (Union[List, Tuple][Any]):
        Returns:
            __HuffmanCoder
        Examples:
            >>> seq = "a"*100 + "b"*29 + "c"*32
            >>> coder = HuffmanCoderFactory.from_sequence(seq)
        """</span>
        frequencies <span class="token operator">=</span> collections<span class="token punctuation">.</span>Counter<span class="token punctuation">(</span>sequence<span class="token punctuation">)</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">.</span>from_frequencies<span class="token punctuation">(</span>frequencies<span class="token punctuation">,</span> concat<span class="token operator">=</span>_guess_concat_function<span class="token punctuation">(</span>sequence<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="4-哈夫曼编码层接口"><a href="#4-哈夫曼编码层接口" class="headerlink" title="4. 哈夫曼编码层接口"></a>4. 哈夫曼编码层接口</h3><p>为实现GUI与哈弗曼编码解耦，在前面哈夫曼编码的核心类的基础上，编写哈弗曼编码接口函数，完成编码、解码一步到位</p>
<p>其中：</p>
<ul>
<li>encode负责读取文件的二进制流、压缩、输出压缩时的摘要信息</li>
<li>decode负责读取文件的二进制流、解压、输出解压时的摘要信息</li>
</ul>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">encode</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">,</span> verbose<span class="token punctuation">:</span> bool<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">assert</span> <span class="token punctuation">(</span>path <span class="token punctuation">:</span><span class="token operator">=</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>exists<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}{path} not exists!{Style.RESET_ALL}"</span>
    <span class="token keyword">with</span> path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">"rb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        content <span class="token operator">=</span> f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    coder <span class="token operator">=</span> HuffmanCoderFactory<span class="token punctuation">.</span>from_sequence<span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    byte <span class="token operator">=</span> coder<span class="token punctuation">.</span>encode<span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> <span class="token punctuation">(</span>out_fd <span class="token punctuation">:</span><span class="token operator">=</span> path<span class="token punctuation">.</span>resolve<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>parent <span class="token operator">/</span> <span class="token punctuation">(</span>path<span class="token punctuation">.</span>stem <span class="token operator">+</span> <span class="token string">"_huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>exists<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        out_fd<span class="token punctuation">.</span>mkdir<span class="token punctuation">(</span>parents<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    <span class="token keyword">with</span> <span class="token punctuation">(</span>main_path <span class="token punctuation">:</span><span class="token operator">=</span> out_fd<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span>path<span class="token punctuation">.</span>stem <span class="token operator">+</span> <span class="token string">"_"</span> <span class="token operator">+</span> path<span class="token punctuation">.</span>suffix<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">".huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>open<span class="token punctuation">(</span><span class="token string">"wb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        f<span class="token punctuation">.</span>write<span class="token punctuation">(</span>byte<span class="token punctuation">)</span>
    coder<span class="token punctuation">.</span>save<span class="token punctuation">(</span>coder_path <span class="token punctuation">:</span><span class="token operator">=</span> out_fd<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span>path<span class="token punctuation">.</span>stem <span class="token operator">+</span> <span class="token string">"_coder.huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

    b1 <span class="token operator">=</span> path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b2 <span class="token operator">=</span> main_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b3 <span class="token operator">=</span> coder_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4 <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">""</span><span class="token punctuation">]</span><span class="token operator">*</span><span class="token number">4</span>
    <span class="token keyword">if</span> verbose<span class="token punctuation">:</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {path.stem}{path.suffix}, byte size: {b1}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Compressed file: {main_path.stem}{main_path.suffix}, byte size: {b2}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"HuffCoder file: {coder_path.stem}{coder_path.suffix}, byte size: {b3}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"{Fore.GREEN}Compression rate {b2}/{b1}={Style.BRIGHT}{round(b2/b1, 4)*100}%{Style.NORMAL}"</span> <span class="token operator">+</span>\
                f<span class="token string">", saved {Style.BRIGHT}{b1-b2}{Style.RESET_ALL} bytes"</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>s <span class="token punctuation">:</span><span class="token operator">=</span> <span class="token string">"Summary"</span><span class="token punctuation">.</span>center<span class="token punctuation">(</span>max<span class="token punctuation">(</span><span class="token punctuation">[</span>len<span class="token punctuation">(</span>summary1<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary2<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary3<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary4<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"="</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">(</span>summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"="</span><span class="token operator">*</span>len<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> b1<span class="token punctuation">,</span> b2<span class="token punctuation">,</span> b3<span class="token punctuation">,</span> coder


<span class="token keyword">def</span> <span class="token function">decode</span><span class="token punctuation">(</span>huf_path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>Path<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>Path<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> verbose<span class="token punctuation">:</span> bool<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    huf_path<span class="token punctuation">,</span> coder_path <span class="token operator">=</span> Path<span class="token punctuation">(</span>huf_path<span class="token punctuation">)</span><span class="token punctuation">,</span> Path<span class="token punctuation">(</span>coder_path<span class="token punctuation">)</span>
    coder <span class="token operator">=</span> _HuffmanCoder<span class="token punctuation">.</span>load<span class="token punctuation">(</span>coder_path<span class="token punctuation">)</span>
    <span class="token keyword">with</span> huf_path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">"rb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        content <span class="token operator">=</span> f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    decoded <span class="token operator">=</span> coder<span class="token punctuation">.</span>decode<span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    <span class="token keyword">with</span> <span class="token punctuation">(</span>dec_path <span class="token punctuation">:</span><span class="token operator">=</span> huf_path<span class="token punctuation">.</span>parent<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span><span class="token punctuation">(</span>s <span class="token punctuation">:</span><span class="token operator">=</span> huf_path<span class="token punctuation">.</span>stem<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string">"_"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">+</span> f<span class="token string">"_decode.{s[1]}"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">"wb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        f<span class="token punctuation">.</span>write<span class="token punctuation">(</span>decoded<span class="token punctuation">)</span>

    b1 <span class="token operator">=</span> huf_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b2 <span class="token operator">=</span> coder_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b3 <span class="token operator">=</span> dec_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    <span class="token keyword">if</span> verbose<span class="token punctuation">:</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {huf_path.stem}{huf_path.suffix}, byte size: {b1}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Coder file: {coder_path.stem}{coder_path.suffix}, byte size: {b2}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"Decompressed file: {dec_path.stem}{dec_path.suffix}, byte size: {b3}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"{Fore.GREEN}Decompression rate {b3}/{b1}={Style.BRIGHT}{round(b3/b1, 4)*100}%{Style.NORMAL}"</span> <span class="token operator">+</span>\
                f<span class="token string">", lost {Style.BRIGHT}{b3-b1}{Style.RESET_ALL} bytes"</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>s <span class="token punctuation">:</span><span class="token operator">=</span> <span class="token string">"Summary"</span><span class="token punctuation">.</span>center<span class="token punctuation">(</span>max<span class="token punctuation">(</span><span class="token punctuation">[</span>len<span class="token punctuation">(</span>summary1<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary2<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary3<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary4<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"="</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">(</span>summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"="</span><span class="token operator">*</span>len<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> b1<span class="token punctuation">,</span> b2<span class="token punctuation">,</span> b3<span class="token punctuation">,</span> coder
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="5-用户接口层"><a href="#5-用户接口层" class="headerlink" title="5. 用户接口层"></a>5. 用户接口层</h3><p>用户接口层负责提供命令行用户界面以及GUI用户界面</p>
<h4 id="A-GUI用户界面"><a href="#A-GUI用户界面" class="headerlink" title="A. GUI用户界面"></a>A. GUI用户界面</h4><p>GUI用户界面由于会接管程序流的控制，因此以类的形式进行封装。</p>
<p>其中：</p>
<ul>
<li>initUI方法负责窗口的UI设计、按钮等控件的信号对应的槽函数（信号处理函数）的绑定</li>
<li>fileDialog方法负责暂时接管程序流，以GUI的形式帮助用户进行文件的选择，以Dialog的形式暂时接管程序运行</li>
<li>compress方法作为压缩按钮的槽函数，负责文件的压缩，在进行文件路径检查（文件存在）之后，调用哈夫曼编码层提供的接口函数进行压缩，并将摘要信息输出到程序显示框内</li>
<li>decompress方法作为解压按钮的槽函数，负责文件的解压，在进行文件路径检查（压缩文件以及哈夫曼编码表文件均存在）之后，调用哈夫曼编码层提供的接口函数进行解压，并将摘要信息输出到程序显示框内</li>
</ul>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">QtGUIHuff</span><span class="token punctuation">(</span>QMainWindow<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        super<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__init__<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>initUI<span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">initUI</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">from</span> Ui_main <span class="token keyword">import</span> Ui_Form
        self<span class="token punctuation">.</span>ui <span class="token operator">=</span> Ui_Form<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>setupUi<span class="token punctuation">(</span>self<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>setWindowTitle<span class="token punctuation">(</span><span class="token string">"哈夫曼压缩/解压缩程序"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>compress<span class="token punctuation">.</span>clicked<span class="token punctuation">.</span>connect<span class="token punctuation">(</span>self<span class="token punctuation">.</span>compress<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>decompress<span class="token punctuation">.</span>clicked<span class="token punctuation">.</span>connect<span class="token punctuation">(</span>self<span class="token punctuation">.</span>decompress<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">fileDialog</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        dialog <span class="token operator">=</span> QFileDialog<span class="token punctuation">(</span><span class="token punctuation">)</span>
        dialog<span class="token punctuation">.</span>setFileMode<span class="token punctuation">(</span>QFileDialog<span class="token punctuation">.</span>AnyFile<span class="token punctuation">)</span>
        dialog<span class="token punctuation">.</span>setFilter<span class="token punctuation">(</span>QDir<span class="token punctuation">.</span>Files<span class="token punctuation">)</span>
        <span class="token keyword">if</span> dialog<span class="token punctuation">.</span><span class="token keyword">exec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
            filename <span class="token operator">=</span> dialog<span class="token punctuation">.</span>selectedFiles<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Path<span class="token punctuation">(</span>filename<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token boolean">False</span>

    <span class="token keyword">def</span> <span class="token function">compress</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        f_path <span class="token operator">=</span> self<span class="token punctuation">.</span>fileDialog<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setReadOnly<span class="token punctuation">(</span><span class="token boolean">True</span><span class="token punctuation">)</span>
        s <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"开始压缩"</span><span class="token punctuation">]</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
        result <span class="token operator">=</span> encode<span class="token punctuation">(</span>f_path<span class="token punctuation">)</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {f_path.stem}{f_path.suffix}, byte size: {result[0]}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Compressed file:  byte size: {result[1]}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"HuffCoder file: , byte size: {result[2]}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"Compression rate {result[1]}/{result[0]}={round(result[1]/result[0], 4)*100}%"</span> <span class="token operator">+</span> \
                f<span class="token string">", saved {result[0] - result[1]} bytes"</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">*</span>s<span class="token punctuation">,</span> summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">,</span> <span class="token string">"压缩结束"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit_2<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>result<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__str__<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">decompress</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        f_path <span class="token operator">=</span> self<span class="token punctuation">.</span>fileDialog<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> f_path<span class="token punctuation">.</span>suffix <span class="token operator">!=</span> <span class="token string">".huf"</span><span class="token punctuation">:</span>
            QMessageBox<span class="token punctuation">.</span>warning<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">"警告"</span><span class="token punctuation">,</span> <span class="token string">"请选择正确的压缩文件路径"</span><span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token boolean">False</span>

        <span class="token keyword">if</span> len<span class="token punctuation">(</span>ps <span class="token punctuation">:</span><span class="token operator">=</span> list<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>f_path<span class="token punctuation">)</span><span class="token punctuation">.</span>parent<span class="token punctuation">.</span>glob<span class="token punctuation">(</span><span class="token string">"*.huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">:</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>ps<span class="token punctuation">)</span>
            <span class="token keyword">for</span> p_idx <span class="token keyword">in</span> range<span class="token punctuation">(</span>len<span class="token punctuation">(</span>ps<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">if</span> <span class="token string">"coder"</span> <span class="token keyword">in</span> ps<span class="token punctuation">[</span>p_idx<span class="token punctuation">]</span><span class="token punctuation">.</span>stem<span class="token punctuation">:</span>
                    cp_idx <span class="token operator">=</span> p_idx
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            QMessageBox<span class="token punctuation">.</span>warning<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">"警告"</span><span class="token punctuation">,</span> <span class="token string">"缺少必要的解压缩文件"</span><span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token boolean">False</span>
        s <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"开始解压"</span><span class="token punctuation">]</span>
        <span class="token keyword">import</span> time
        time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
        result <span class="token operator">=</span> decode<span class="token punctuation">(</span>huf_path<span class="token punctuation">:</span><span class="token operator">=</span>ps<span class="token punctuation">[</span><span class="token number">1</span><span class="token operator">-</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token punctuation">:</span><span class="token operator">=</span>ps<span class="token punctuation">[</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">)</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {huf_path.stem}{huf_path.suffix}, byte size: {result[0]}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Coder file: {coder_path.stem}{coder_path.suffix}, byte size: {result[1]}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"Decompressed file: byte size: {result[2]}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"Decompression rate {result[2]}/{result[0]}={round(result[2]/result[0], 4)*100}%"</span> <span class="token operator">+</span>\
                f<span class="token string">", lost {result[2] - result[0]} bytes"</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">*</span>s<span class="token punctuation">,</span> summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">,</span> <span class="token string">"解压结束"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit_2<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>result<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__str__<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h4 id="B-Terminal用户界面"><a href="#B-Terminal用户界面" class="headerlink" title="B. Terminal用户界面"></a>B. Terminal用户界面</h4><p>Terminal用户界面则是以argparse库中的ArgumentParser对象完成命令行参数的接受，稍后在主程序中配合完成用户界面。设定了-h参数输出帮助信息、-s对指定路径的文件进行压缩、-t对指定路径的文件进行解压、-g以GUI用户界面运行程序，-v将压缩/解压摘要输出到命令行窗口</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">parse_args</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> argparse<span class="token punctuation">.</span>Namespace<span class="token punctuation">:</span>
    parser <span class="token operator">=</span> argparse<span class="token punctuation">.</span>ArgumentParser<span class="token punctuation">(</span>
        description<span class="token operator">=</span><span class="token string">"哈夫曼压缩算法Python实现，可以对任意文件进行压缩，提供命令行以及GUI用户界面。作者：Jack Wang"</span>
    <span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-s"</span><span class="token punctuation">,</span> <span class="token string">"--src"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"src_path"</span><span class="token punctuation">,</span> type<span class="token operator">=</span>str<span class="token punctuation">,</span> default<span class="token operator">=</span>None<span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"需要压缩的文件路径"</span><span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-t"</span><span class="token punctuation">,</span> <span class="token string">"--target"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"target_path"</span><span class="token punctuation">,</span> type<span class="token operator">=</span>str<span class="token punctuation">,</span> default<span class="token operator">=</span>None<span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"需要解压文件所在的文件夹的路径"</span><span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-v"</span><span class="token punctuation">,</span> <span class="token string">"--verbose"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"v"</span><span class="token punctuation">,</span> action<span class="token operator">=</span><span class="token string">"store_true"</span><span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"是否显示压缩的摘要信息"</span><span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-g"</span><span class="token punctuation">,</span> <span class="token string">"--gui"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"gui"</span><span class="token punctuation">,</span> action<span class="token operator">=</span><span class="token string">"store_true"</span><span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"是否以GUI方式显示"</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> parser<span class="token punctuation">.</span>parse_args<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="6-主程序"><a href="#6-主程序" class="headerlink" title="6. 主程序"></a>6. 主程序</h3><p>主程序很简单，根据接受的命令行参数调用不同的接口即可</p>
<p>在非图形界面的运行的时候，程序的主控制流以程序为主，而以GUI方式运行的时候，程序的控制流则在Qt框架下，用户级线程调度、事件监听、信号分发、槽函数（信号处理函数）唤醒等均由框架负责。</p>
<pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> argparse<span class="token punctuation">.</span>Namespace<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> args<span class="token punctuation">.</span>gui<span class="token punctuation">:</span>
        <span class="token keyword">assert</span> <span class="token number">0</span> <span class="token operator">&lt;</span> int<span class="token punctuation">(</span>args<span class="token punctuation">.</span>src_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">)</span> <span class="token operator">+</span> int<span class="token punctuation">(</span>args<span class="token punctuation">.</span>target_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}请选择压缩或者解压，使用-h查询用法{Style.RESET_ALL}"</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> args<span class="token punctuation">.</span>gui<span class="token punctuation">:</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"{Fore.GREEN}命令行工作模式{Style.RESET_ALL}"</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> args<span class="token punctuation">.</span>src_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"开始压缩 {args.src_path}"</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> args<span class="token punctuation">.</span>v<span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>encode<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>args<span class="token punctuation">.</span>src_path<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                encode<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>args<span class="token punctuation">.</span>src_path<span class="token punctuation">)</span><span class="token punctuation">,</span> verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"压缩完成"</span><span class="token punctuation">)</span>
        <span class="token keyword">elif</span> args<span class="token punctuation">.</span>target_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">if</span> len<span class="token punctuation">(</span>ps <span class="token punctuation">:</span><span class="token operator">=</span> list<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>args<span class="token punctuation">.</span>target_path<span class="token punctuation">)</span><span class="token punctuation">.</span>glob<span class="token punctuation">(</span><span class="token string">"*.huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>ps<span class="token punctuation">)</span>
                <span class="token keyword">for</span> p_idx <span class="token keyword">in</span> range<span class="token punctuation">(</span>len<span class="token punctuation">(</span>ps<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                    <span class="token keyword">if</span> <span class="token string">"coder"</span> <span class="token keyword">in</span> ps<span class="token punctuation">[</span>p_idx<span class="token punctuation">]</span><span class="token punctuation">.</span>stem<span class="token punctuation">:</span>
                        cp_idx <span class="token operator">=</span> p_idx
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                <span class="token keyword">assert</span> <span class="token boolean">False</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}无效的路径，没有找到压缩文件{Style.RESET_ALL}"</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"开始解压 {args.target_path}"</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> args<span class="token punctuation">.</span>v<span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>decode<span class="token punctuation">(</span>huf_path<span class="token operator">=</span>ps<span class="token punctuation">[</span><span class="token number">1</span><span class="token operator">-</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token operator">=</span>ps<span class="token punctuation">[</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                decode<span class="token punctuation">(</span>huf_path<span class="token operator">=</span>ps<span class="token punctuation">[</span><span class="token number">1</span><span class="token operator">-</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token operator">=</span>ps<span class="token punctuation">[</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"解压完成"</span><span class="token punctuation">)</span>
    <span class="token keyword">else</span><span class="token punctuation">:</span>
        app <span class="token operator">=</span> QApplication<span class="token punctuation">(</span>sys<span class="token punctuation">.</span>argv<span class="token punctuation">)</span>
        m <span class="token operator">=</span> QtGUIHuff<span class="token punctuation">(</span><span class="token punctuation">)</span>
        m<span class="token punctuation">.</span>show<span class="token punctuation">(</span><span class="token punctuation">)</span>
        sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span>app<span class="token punctuation">.</span>exec_<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>




<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>
    main<span class="token punctuation">(</span>parse_args<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h2 id="5-运行实例"><a href="#5-运行实例" class="headerlink" title="5. 运行实例"></a>5. 运行实例</h2><h3 id="A-GIF版演示"><a href="#A-GIF版演示" class="headerlink" title="A. GIF版演示"></a>A. GIF版演示</h3><p> 这里先放一个gif图片（比较大，44兆，不知道能不能加载出来），高清版本去下面的B站视频里看看</p>
<p><img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/哈夫曼算法压缩任意文件演示.gif" alt=""></p>
<h3 id="B-B站高清版"><a href="#B-B站高清版" class="headerlink" title="B. B站高清版"></a>B. B站高清版</h3><p><a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1cL41177zL/">视频链接</a>：<a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1cL41177zL/">https://www.bilibili.com/video/BV1cL41177zL/</a></p>
<iframe src="//player.bilibili.com/player.html?aid=465019836&amp;bvid=BV1cL41177zL&amp;cid=465905780&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>





<h2 id="6-完整代码"><a href="#6-完整代码" class="headerlink" title="6. 完整代码"></a>6. 完整代码</h2><p>完整代码如下</p>
<h3 id="A-huff-py"><a href="#A-huff-py" class="headerlink" title="A. huff.py"></a>A. huff.py</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token keyword">import</span> sys
<span class="token keyword">import</span> pickle
<span class="token keyword">import</span> argparse
<span class="token keyword">import</span> itertools
<span class="token keyword">import</span> collections
<span class="token keyword">from</span> pathlib <span class="token keyword">import</span> Path
<span class="token keyword">from</span> heapq <span class="token keyword">import</span> heapify<span class="token punctuation">,</span> heappush<span class="token punctuation">,</span> heappop
<span class="token keyword">from</span> typing <span class="token keyword">import</span> Union<span class="token punctuation">,</span> Any<span class="token punctuation">,</span> Dict<span class="token punctuation">,</span> List<span class="token punctuation">,</span> Tuple
<span class="token keyword">from</span> PyQt5<span class="token punctuation">.</span>QtCore <span class="token keyword">import</span> QDir

<span class="token keyword">import</span> pandas
<span class="token keyword">import</span> pandas <span class="token keyword">as</span> pd
<span class="token keyword">from</span> colorama <span class="token keyword">import</span> Fore<span class="token punctuation">,</span> Style
<span class="token keyword">from</span> PyQt5<span class="token punctuation">.</span>QtWidgets <span class="token keyword">import</span> <span class="token operator">*</span>

__author__ <span class="token operator">=</span> <span class="token string">"Jack Wang"</span>
__date__ <span class="token operator">=</span> <span class="token string">"2021/12/19"</span>
sys<span class="token punctuation">.</span>path<span class="token punctuation">.</span>append<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>__file__<span class="token punctuation">)</span><span class="token punctuation">.</span>resolve<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>parent<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span><span class="token string">"./ui"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__str__<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>


<span class="token keyword">class</span> <span class="token class-name">_EOFSymbol</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        _EOFSymbol 是内置的文件结尾的类，用于在编码的时候标志文件的结尾。
        由于在进行哈夫曼树的构建的时候需要选出来权最小的节点，因此需要重写比较的魔术方法。
        默认文档结尾是频率最少的字符，因此和任何数字比较
    """</span>

    <span class="token keyword">def</span> <span class="token function">__repr__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token string">"_EOF"</span>

    <span class="token keyword">def</span> <span class="token function">__lt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token boolean">True</span>

    <span class="token keyword">def</span> <span class="token function">__gt__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token boolean">False</span>

    <span class="token keyword">def</span> <span class="token function">__eq__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> other<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>__class__ <span class="token operator">==</span> other<span class="token punctuation">.</span>__class__

    <span class="token keyword">def</span> <span class="token function">__hash__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> hash<span class="token punctuation">(</span>self<span class="token punctuation">.</span>__class__<span class="token punctuation">)</span>


_EOF <span class="token operator">=</span> _EOFSymbol<span class="token punctuation">(</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">_guess_concat_function</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        To support multiple types of input, e.g., str, bytes, list, there must be a
        function that return the needed concat function
    Returns:
        pass
    """</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>type<span class="token punctuation">(</span>u<span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span> u<span class="token string">""</span><span class="token punctuation">.</span>join<span class="token punctuation">,</span> type<span class="token punctuation">(</span>b<span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">:</span> bytes<span class="token punctuation">}</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span>type<span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">,</span> list<span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">ensure_dir</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Path<span class="token punctuation">:</span>
    path<span class="token punctuation">:</span> Path <span class="token operator">=</span> path <span class="token keyword">if</span> isinstance<span class="token punctuation">(</span>path<span class="token punctuation">,</span> Path<span class="token punctuation">)</span> <span class="token keyword">else</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> path<span class="token punctuation">.</span>exists<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        path<span class="token punctuation">.</span>mkdir<span class="token punctuation">(</span>parents<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    <span class="token keyword">assert</span> path<span class="token punctuation">.</span>is_dir<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> path


<span class="token keyword">class</span> <span class="token class-name">_HuffmanCoder</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        _HuffmanCoder, which provide encode, decode, serialize, deserialize, save_code, load_code functionalities.
    Args:

    """</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> code_table<span class="token punctuation">:</span> Dict<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> concat<span class="token operator">=</span>list<span class="token punctuation">,</span> check<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">,</span> eof<span class="token operator">=</span>_EOF<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            initialize method of _HuffmanCoder with given code table
        Args:
            code_table (Dict[Any, Tuple[int, int]]): code table of all symbols in the encoding sequence, create automatically by
                HuffmanCoderFactory, items are Tuple[Symbol, [depth, bit]]
            concat: concat method of symbols, will be automatically determined in HuffmanCoderFactory
            check: valid code table's format
            eof: eof symbol, leave it alone
        """</span>
        self<span class="token punctuation">.</span>_table <span class="token operator">=</span> code_table
        self<span class="token punctuation">.</span>_concat <span class="token operator">=</span> concat
        self<span class="token punctuation">.</span>_eof <span class="token operator">=</span> eof
        <span class="token keyword">if</span> check<span class="token punctuation">:</span>
            <span class="token keyword">assert</span> isinstance<span class="token punctuation">(</span>self<span class="token punctuation">.</span>_table<span class="token punctuation">,</span> dict<span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}Code table need to be a dict!{Style.RESET_ALL}"</span>
            <span class="token keyword">assert</span> all<span class="token punctuation">(</span>
                isinstance<span class="token punctuation">(</span>b<span class="token punctuation">,</span> int<span class="token punctuation">)</span> <span class="token operator">and</span> b <span class="token operator">>=</span> <span class="token number">1</span> <span class="token operator">and</span> isinstance<span class="token punctuation">(</span>v<span class="token punctuation">,</span> int<span class="token punctuation">)</span> <span class="token operator">and</span> v <span class="token operator">>=</span> <span class="token number">0</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">.</span>values<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}Code table internal format Error!{Style.RESET_ALL}"</span>

    <span class="token keyword">def</span> <span class="token function">get_code_table</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Dict<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            get_code_table returns code table of input sequence
        Returns:
            self._table
        """</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>_table

    <span class="token keyword">def</span> <span class="token function">print_code_table</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> pandas<span class="token punctuation">.</span>DataFrame<span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            print_code_table is used to print code table
        Args:
            verbose (bool): if True, then print the table in a pretty format
        Returns:
            pd.DataFrame
        """</span>
        pd<span class="token punctuation">.</span>options<span class="token punctuation">.</span>display<span class="token punctuation">.</span>max_rows <span class="token operator">=</span> None
        columns <span class="token operator">=</span> list<span class="token punctuation">(</span>zip<span class="token punctuation">(</span><span class="token operator">*</span>list<span class="token punctuation">(</span>zip<span class="token punctuation">(</span><span class="token operator">*</span>itertools<span class="token punctuation">.</span>chain<span class="token punctuation">(</span>
            <span class="token punctuation">[</span><span class="token punctuation">(</span><span class="token string">"Symbol"</span><span class="token punctuation">,</span> <span class="token string">"Huff Code"</span><span class="token punctuation">,</span> <span class="token string">"BitSize"</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>
                <span class="token punctuation">(</span>repr<span class="token punctuation">(</span>symbol<span class="token punctuation">)</span><span class="token punctuation">,</span> bin<span class="token punctuation">(</span>val<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">.</span>rjust<span class="token punctuation">(</span>bits<span class="token punctuation">,</span> <span class="token string">"0"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> str<span class="token punctuation">(</span>bits<span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token keyword">for</span> symbol<span class="token punctuation">,</span> <span class="token punctuation">(</span>bits<span class="token punctuation">,</span> val<span class="token punctuation">)</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">)</span>
        <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        df <span class="token operator">=</span> pd<span class="token punctuation">.</span>DataFrame<span class="token punctuation">(</span>columns<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">]</span><span class="token punctuation">,</span> columns<span class="token operator">=</span>columns<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> verbose<span class="token punctuation">:</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>df<span class="token punctuation">)</span>
        <span class="token keyword">return</span> df

    <span class="token keyword">def</span> <span class="token function">_encode_streaming</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> raw_sequence<span class="token punctuation">)</span><span class="token punctuation">:</span>
        size <span class="token operator">=</span> <span class="token number">0</span>
        buffer <span class="token operator">=</span> <span class="token number">0</span>
        <span class="token keyword">for</span> s <span class="token keyword">in</span> raw_sequence<span class="token punctuation">:</span>
            bits<span class="token punctuation">,</span> values <span class="token operator">=</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">[</span>s<span class="token punctuation">]</span>
            buffer <span class="token operator">=</span> <span class="token punctuation">(</span>buffer <span class="token operator">&lt;&lt;</span> bits<span class="token punctuation">)</span> <span class="token operator">+</span> values
            size <span class="token operator">+=</span> bits
            <span class="token keyword">while</span> size <span class="token operator">>=</span> <span class="token number">8</span><span class="token punctuation">:</span>
                byte <span class="token operator">=</span> buffer <span class="token operator">>></span> <span class="token punctuation">(</span>size <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span>
                <span class="token keyword">yield</span> byte
                buffer <span class="token operator">=</span> buffer <span class="token operator">-</span> <span class="token punctuation">(</span>byte <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span>size <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                size <span class="token operator">-=</span> <span class="token number">8</span>
        <span class="token keyword">if</span> size <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">:</span>
            bit<span class="token punctuation">,</span> value <span class="token operator">=</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">[</span>self<span class="token punctuation">.</span>_eof<span class="token punctuation">]</span>
            buffer <span class="token operator">=</span> <span class="token punctuation">(</span>buffer <span class="token operator">&lt;&lt;</span> bit<span class="token punctuation">)</span> <span class="token operator">+</span> value
            size <span class="token operator">+=</span> bit
            <span class="token keyword">if</span> size <span class="token operator">></span> <span class="token number">8</span><span class="token punctuation">:</span>
                byte <span class="token operator">=</span> buffer <span class="token operator">>></span> <span class="token punctuation">(</span>size <span class="token operator">-</span> <span class="token number">8</span><span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                byte <span class="token operator">=</span> buffer <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span><span class="token number">8</span> <span class="token operator">-</span> size<span class="token punctuation">)</span>
            <span class="token keyword">yield</span> byte

    <span class="token keyword">def</span> <span class="token function">_decode_streaming</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> encoded_sequence<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token comment" spellcheck="true"># Reverse lookup table: map (bitsize, value) to symbols</span>
        lookup <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">(</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">:</span> s <span class="token keyword">for</span> s<span class="token punctuation">,</span> <span class="token punctuation">(</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>_table<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>

        buffer <span class="token operator">=</span> <span class="token number">0</span>
        size <span class="token operator">=</span> <span class="token number">0</span>
        <span class="token keyword">for</span> byte <span class="token keyword">in</span> encoded_sequence<span class="token punctuation">:</span>
            <span class="token keyword">for</span> m <span class="token keyword">in</span> <span class="token punctuation">[</span><span class="token number">128</span><span class="token punctuation">,</span> <span class="token number">64</span><span class="token punctuation">,</span> <span class="token number">32</span><span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
                buffer <span class="token operator">=</span> <span class="token punctuation">(</span>buffer <span class="token operator">&lt;&lt;</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> bool<span class="token punctuation">(</span>byte <span class="token operator">&amp;</span> m<span class="token punctuation">)</span>
                size <span class="token operator">+=</span> <span class="token number">1</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>size<span class="token punctuation">,</span> buffer<span class="token punctuation">)</span> <span class="token keyword">in</span> lookup<span class="token punctuation">:</span>
                    symbol <span class="token operator">=</span> lookup<span class="token punctuation">[</span>size<span class="token punctuation">,</span> buffer<span class="token punctuation">]</span>
                    <span class="token keyword">if</span> symbol <span class="token operator">==</span> self<span class="token punctuation">.</span>_eof<span class="token punctuation">:</span>
                        <span class="token keyword">return</span>
                    <span class="token keyword">yield</span> symbol
                    buffer <span class="token operator">=</span> <span class="token number">0</span>
                    size <span class="token operator">=</span> <span class="token number">0</span>

    <span class="token keyword">def</span> <span class="token function">encode</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> raw_seq<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            encode is used to encode the sequence that build the huffman tree
        Args:
            raw_seq: sequence that build the huffman tree
        Returns:
            bytes
        """</span>
        <span class="token keyword">return</span> bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>_encode_streaming<span class="token punctuation">(</span>raw_seq<span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">decode</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> encoded_seq<span class="token punctuation">,</span> concat<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token punctuation">(</span>concat <span class="token operator">or</span> self<span class="token punctuation">.</span>_concat<span class="token punctuation">)</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>_decode_streaming<span class="token punctuation">(</span>encoded_seq<span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">save</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">,</span> metadata<span class="token punctuation">:</span> Any <span class="token operator">=</span> None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        code_table <span class="token operator">=</span> self<span class="token punctuation">.</span>get_code_table<span class="token punctuation">(</span><span class="token punctuation">)</span>
        data <span class="token operator">=</span> <span class="token punctuation">{</span>
            <span class="token string">"code_table"</span><span class="token punctuation">:</span> code_table<span class="token punctuation">,</span>
            <span class="token string">"type"</span><span class="token punctuation">:</span> type<span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">,</span>
            <span class="token string">"concat"</span><span class="token punctuation">:</span> self<span class="token punctuation">.</span>_concat<span class="token punctuation">,</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> metadata<span class="token punctuation">:</span>
            data<span class="token punctuation">[</span><span class="token string">'metadata'</span><span class="token punctuation">]</span> <span class="token operator">=</span> metadata
        path <span class="token operator">=</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
        ensure_dir<span class="token punctuation">(</span>path<span class="token punctuation">.</span>parent<span class="token punctuation">)</span>
        <span class="token keyword">with</span> path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">'wb'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
            pickle<span class="token punctuation">.</span>dump<span class="token punctuation">(</span>data<span class="token punctuation">,</span> file<span class="token operator">=</span>f<span class="token punctuation">)</span>

    @staticmethod
    <span class="token keyword">def</span> <span class="token function">load</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token string">'_HuffmanCoder'</span><span class="token punctuation">:</span>
        path <span class="token operator">=</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span>
        <span class="token keyword">with</span> path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">'rb'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
            data <span class="token operator">=</span> pickle<span class="token punctuation">.</span>load<span class="token punctuation">(</span>f<span class="token punctuation">)</span>
        cls <span class="token operator">=</span> data<span class="token punctuation">[</span><span class="token string">'type'</span><span class="token punctuation">]</span>
        <span class="token keyword">assert</span> issubclass<span class="token punctuation">(</span>cls<span class="token punctuation">,</span> _HuffmanCoder<span class="token punctuation">)</span>
        code_table <span class="token operator">=</span> data<span class="token punctuation">[</span><span class="token string">'code_table'</span><span class="token punctuation">]</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">(</span>code_table<span class="token punctuation">,</span> concat<span class="token operator">=</span>data<span class="token punctuation">[</span><span class="token string">'concat'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>


<span class="token keyword">class</span> <span class="token class-name">HuffmanCoderFactory</span><span class="token punctuation">(</span>_HuffmanCoder<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    Notes:
        HuffmanCodecFactory is the class that create HuffmanCoder from different type of inputs
    Methods:
        from_frequencies: create HuffmanCoder from frequency table
    """</span>

    @classmethod
    <span class="token keyword">def</span> <span class="token function">from_frequencies</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> frequencies<span class="token punctuation">:</span> Dict<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">,</span> concat<span class="token operator">=</span>None<span class="token punctuation">,</span> eof<span class="token operator">=</span>_EOF<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            from_frequencies creates huffman codec by symbol-frequency table/mapping.
        Args:
            frequencies (Dict[Any, int]): Symbols and its frequency, symbols can be str, bytes or int, etc.
            concat (Union[None]): concat method of symbols, will be determined by the function if argument is not
                specified
            eof (_EOFSymbol): leave it alone.
        Returns:
            __HuffmanCoder
        Examples:
            >>> huf_coder = HuffmanCoderFactory.from_frequencies({"a":29, "b":10, "c": 5})
            >>> type(huf_coder)
        """</span>
        concat_function <span class="token operator">=</span> concat <span class="token keyword">if</span> concat <span class="token keyword">is</span> <span class="token operator">not</span> None <span class="token keyword">else</span> _guess_concat_function<span class="token punctuation">(</span>next<span class="token punctuation">(</span>iter<span class="token punctuation">(</span>frequencies<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

        <span class="token comment" spellcheck="true"># build huffman tree node heap</span>
        <span class="token comment" spellcheck="true"># each item: (frequency, [(symbol, (bitsize, value))], value equals which layer of the tree</span>
        heap<span class="token punctuation">:</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>freq<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>symbol<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">for</span> symbol<span class="token punctuation">,</span> freq <span class="token keyword">in</span>
                                                                     frequencies<span class="token punctuation">.</span>items<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
        <span class="token comment" spellcheck="true"># add eof</span>
        <span class="token keyword">if</span> eof <span class="token operator">not</span> <span class="token keyword">in</span> frequencies<span class="token punctuation">:</span>
            heap<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>eof<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

        heapify<span class="token punctuation">(</span>heap<span class="token punctuation">)</span>
        <span class="token keyword">while</span> len<span class="token punctuation">(</span>heap<span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">1</span><span class="token punctuation">:</span>
            <span class="token comment" spellcheck="true"># get first 2 min as left and right child tree</span>
            a<span class="token punctuation">:</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> heappop<span class="token punctuation">(</span>heap<span class="token punctuation">)</span>
            b<span class="token punctuation">:</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> List<span class="token punctuation">[</span>Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>int<span class="token punctuation">,</span> int<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> heappop<span class="token punctuation">(</span>heap<span class="token punctuation">)</span>
            <span class="token comment" spellcheck="true"># merge child to form parent</span>
            <span class="token comment" spellcheck="true"># parent frequencies adds together, left child add 0 ahead (do nothing) of previous bits</span>
            <span class="token comment" spellcheck="true"># right add 1 ahead of previous bits</span>
            merged <span class="token operator">=</span> <span class="token punctuation">(</span>
                a<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">+</span> b<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
                <span class="token punctuation">[</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">in</span> a<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">&lt;&lt;</span> n<span class="token punctuation">)</span> <span class="token operator">+</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>s<span class="token punctuation">,</span> <span class="token punctuation">(</span>n<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">in</span> b<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
            <span class="token punctuation">)</span>
            heappush<span class="token punctuation">(</span>heap<span class="token punctuation">,</span> merged<span class="token punctuation">)</span>

        <span class="token comment" spellcheck="true"># code table is root</span>
        table <span class="token operator">=</span> dict<span class="token punctuation">(</span>heappop<span class="token punctuation">(</span>heap<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">(</span>table<span class="token punctuation">,</span> concat<span class="token operator">=</span>concat<span class="token punctuation">,</span> check<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">,</span> eof<span class="token operator">=</span>eof<span class="token punctuation">)</span>

    @classmethod
    <span class="token keyword">def</span> <span class="token function">from_sequence</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> sequence<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>List<span class="token punctuation">[</span>Any<span class="token punctuation">]</span><span class="token punctuation">,</span> Tuple<span class="token punctuation">[</span>Any<span class="token punctuation">]</span><span class="token punctuation">,</span> str<span class="token punctuation">,</span> bytes<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""
        Notes:
            from_sequence will build a huffman tree from a sequence of symbol
        Args:
            sequence (Union[List, Tuple][Any]):
        Returns:
            __HuffmanCoder
        Examples:
            >>> seq = "a"*100 + "b"*29 + "c"*32
            >>> coder = HuffmanCoderFactory.from_sequence(seq)
        """</span>
        frequencies <span class="token operator">=</span> collections<span class="token punctuation">.</span>Counter<span class="token punctuation">(</span>sequence<span class="token punctuation">)</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">.</span>from_frequencies<span class="token punctuation">(</span>frequencies<span class="token punctuation">,</span> concat<span class="token operator">=</span>_guess_concat_function<span class="token punctuation">(</span>sequence<span class="token punctuation">)</span><span class="token punctuation">)</span>


<span class="token keyword">def</span> <span class="token function">encode</span><span class="token punctuation">(</span>path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>str<span class="token punctuation">,</span> Path<span class="token punctuation">]</span><span class="token punctuation">,</span> verbose<span class="token punctuation">:</span> bool<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">assert</span> <span class="token punctuation">(</span>path <span class="token punctuation">:</span><span class="token operator">=</span> Path<span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>exists<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}{path} not exists!{Style.RESET_ALL}"</span>
    <span class="token keyword">with</span> path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">"rb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        content <span class="token operator">=</span> f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    coder <span class="token operator">=</span> HuffmanCoderFactory<span class="token punctuation">.</span>from_sequence<span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    byte <span class="token operator">=</span> coder<span class="token punctuation">.</span>encode<span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> <span class="token punctuation">(</span>out_fd <span class="token punctuation">:</span><span class="token operator">=</span> path<span class="token punctuation">.</span>resolve<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>parent <span class="token operator">/</span> <span class="token punctuation">(</span>path<span class="token punctuation">.</span>stem <span class="token operator">+</span> <span class="token string">"_huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>exists<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        out_fd<span class="token punctuation">.</span>mkdir<span class="token punctuation">(</span>parents<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    <span class="token keyword">with</span> <span class="token punctuation">(</span>main_path <span class="token punctuation">:</span><span class="token operator">=</span> out_fd<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span>path<span class="token punctuation">.</span>stem <span class="token operator">+</span> <span class="token string">"_"</span> <span class="token operator">+</span> path<span class="token punctuation">.</span>suffix<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">:</span><span class="token punctuation">]</span> <span class="token operator">+</span> <span class="token string">".huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>open<span class="token punctuation">(</span><span class="token string">"wb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        f<span class="token punctuation">.</span>write<span class="token punctuation">(</span>byte<span class="token punctuation">)</span>
    coder<span class="token punctuation">.</span>save<span class="token punctuation">(</span>coder_path <span class="token punctuation">:</span><span class="token operator">=</span> out_fd<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span>path<span class="token punctuation">.</span>stem <span class="token operator">+</span> <span class="token string">"_coder.huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

    b1 <span class="token operator">=</span> path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b2 <span class="token operator">=</span> main_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b3 <span class="token operator">=</span> coder_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4 <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">""</span><span class="token punctuation">]</span><span class="token operator">*</span><span class="token number">4</span>
    <span class="token keyword">if</span> verbose<span class="token punctuation">:</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {path.stem}{path.suffix}, byte size: {b1}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Compressed file: {main_path.stem}{main_path.suffix}, byte size: {b2}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"HuffCoder file: {coder_path.stem}{coder_path.suffix}, byte size: {b3}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"{Fore.GREEN}Compression rate {b2}/{b1}={Style.BRIGHT}{round(b2/b1, 4)*100}%{Style.NORMAL}"</span> <span class="token operator">+</span>\
                f<span class="token string">", saved {Style.BRIGHT}{b1-b2}{Style.RESET_ALL} bytes"</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>s <span class="token punctuation">:</span><span class="token operator">=</span> <span class="token string">"Summary"</span><span class="token punctuation">.</span>center<span class="token punctuation">(</span>max<span class="token punctuation">(</span><span class="token punctuation">[</span>len<span class="token punctuation">(</span>summary1<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary2<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary3<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary4<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"="</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">(</span>summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"="</span><span class="token operator">*</span>len<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> b1<span class="token punctuation">,</span> b2<span class="token punctuation">,</span> b3<span class="token punctuation">,</span> coder


<span class="token keyword">def</span> <span class="token function">decode</span><span class="token punctuation">(</span>huf_path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>Path<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token punctuation">:</span> Union<span class="token punctuation">[</span>Path<span class="token punctuation">,</span> str<span class="token punctuation">]</span><span class="token punctuation">,</span> verbose<span class="token punctuation">:</span> bool<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    huf_path<span class="token punctuation">,</span> coder_path <span class="token operator">=</span> Path<span class="token punctuation">(</span>huf_path<span class="token punctuation">)</span><span class="token punctuation">,</span> Path<span class="token punctuation">(</span>coder_path<span class="token punctuation">)</span>
    coder <span class="token operator">=</span> _HuffmanCoder<span class="token punctuation">.</span>load<span class="token punctuation">(</span>coder_path<span class="token punctuation">)</span>
    <span class="token keyword">with</span> huf_path<span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">"rb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        content <span class="token operator">=</span> f<span class="token punctuation">.</span>read<span class="token punctuation">(</span><span class="token punctuation">)</span>
    decoded <span class="token operator">=</span> coder<span class="token punctuation">.</span>decode<span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    <span class="token keyword">with</span> <span class="token punctuation">(</span>dec_path <span class="token punctuation">:</span><span class="token operator">=</span> huf_path<span class="token punctuation">.</span>parent<span class="token punctuation">.</span>joinpath<span class="token punctuation">(</span><span class="token punctuation">(</span>s <span class="token punctuation">:</span><span class="token operator">=</span> huf_path<span class="token punctuation">.</span>stem<span class="token punctuation">.</span>split<span class="token punctuation">(</span><span class="token string">"_"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">+</span> f<span class="token string">"_decode.{s[1]}"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>open<span class="token punctuation">(</span>mode<span class="token operator">=</span><span class="token string">"wb"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> f<span class="token punctuation">:</span>
        f<span class="token punctuation">.</span>write<span class="token punctuation">(</span>decoded<span class="token punctuation">)</span>

    b1 <span class="token operator">=</span> huf_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b2 <span class="token operator">=</span> coder_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    b3 <span class="token operator">=</span> dec_path<span class="token punctuation">.</span>stat<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>st_size
    <span class="token keyword">if</span> verbose<span class="token punctuation">:</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {huf_path.stem}{huf_path.suffix}, byte size: {b1}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Coder file: {coder_path.stem}{coder_path.suffix}, byte size: {b2}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"Decompressed file: {dec_path.stem}{dec_path.suffix}, byte size: {b3}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"{Fore.GREEN}Decompression rate {b3}/{b1}={Style.BRIGHT}{round(b3/b1, 4)*100}%{Style.NORMAL}"</span> <span class="token operator">+</span>\
                f<span class="token string">", lost {Style.BRIGHT}{b3-b1}{Style.RESET_ALL} bytes"</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>s <span class="token punctuation">:</span><span class="token operator">=</span> <span class="token string">"Summary"</span><span class="token punctuation">.</span>center<span class="token punctuation">(</span>max<span class="token punctuation">(</span><span class="token punctuation">[</span>len<span class="token punctuation">(</span>summary1<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary2<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary3<span class="token punctuation">)</span><span class="token punctuation">,</span> len<span class="token punctuation">(</span>summary4<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"="</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">(</span>summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"="</span><span class="token operator">*</span>len<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> b1<span class="token punctuation">,</span> b2<span class="token punctuation">,</span> b3<span class="token punctuation">,</span> coder



<span class="token keyword">def</span> <span class="token function">parse_args</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> argparse<span class="token punctuation">.</span>Namespace<span class="token punctuation">:</span>
    parser <span class="token operator">=</span> argparse<span class="token punctuation">.</span>ArgumentParser<span class="token punctuation">(</span>
        description<span class="token operator">=</span><span class="token string">"哈夫曼压缩算法Python实现，可以对任意文件进行压缩，提供命令行以及GUI用户界面。作者：Jack Wang"</span>
    <span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-s"</span><span class="token punctuation">,</span> <span class="token string">"--src"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"src_path"</span><span class="token punctuation">,</span> type<span class="token operator">=</span>str<span class="token punctuation">,</span> default<span class="token operator">=</span>None<span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"需要压缩的文件路径"</span><span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-t"</span><span class="token punctuation">,</span> <span class="token string">"--target"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"target_path"</span><span class="token punctuation">,</span> type<span class="token operator">=</span>str<span class="token punctuation">,</span> default<span class="token operator">=</span>None<span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"需要解压文件所在的文件夹的路径"</span><span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-v"</span><span class="token punctuation">,</span> <span class="token string">"--verbose"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"v"</span><span class="token punctuation">,</span> action<span class="token operator">=</span><span class="token string">"store_true"</span><span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"是否显示压缩的摘要信息"</span><span class="token punctuation">)</span>
    parser<span class="token punctuation">.</span>add_argument<span class="token punctuation">(</span><span class="token string">"-g"</span><span class="token punctuation">,</span> <span class="token string">"--gui"</span><span class="token punctuation">,</span> dest<span class="token operator">=</span><span class="token string">"gui"</span><span class="token punctuation">,</span> action<span class="token operator">=</span><span class="token string">"store_true"</span><span class="token punctuation">,</span> help<span class="token operator">=</span><span class="token string">"是否以GUI方式显示"</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> parser<span class="token punctuation">.</span>parse_args<span class="token punctuation">(</span><span class="token punctuation">)</span>


<span class="token keyword">class</span> <span class="token class-name">QtGUIHuff</span><span class="token punctuation">(</span>QMainWindow<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> None<span class="token punctuation">:</span>
        super<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__init__<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>initUI<span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">initUI</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">from</span> Ui_main <span class="token keyword">import</span> Ui_Form
        self<span class="token punctuation">.</span>ui <span class="token operator">=</span> Ui_Form<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>setupUi<span class="token punctuation">(</span>self<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>setWindowTitle<span class="token punctuation">(</span><span class="token string">"哈夫曼压缩/解压缩程序"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>compress<span class="token punctuation">.</span>clicked<span class="token punctuation">.</span>connect<span class="token punctuation">(</span>self<span class="token punctuation">.</span>compress<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>decompress<span class="token punctuation">.</span>clicked<span class="token punctuation">.</span>connect<span class="token punctuation">(</span>self<span class="token punctuation">.</span>decompress<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">fileDialog</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        dialog <span class="token operator">=</span> QFileDialog<span class="token punctuation">(</span><span class="token punctuation">)</span>
        dialog<span class="token punctuation">.</span>setFileMode<span class="token punctuation">(</span>QFileDialog<span class="token punctuation">.</span>AnyFile<span class="token punctuation">)</span>
        dialog<span class="token punctuation">.</span>setFilter<span class="token punctuation">(</span>QDir<span class="token punctuation">.</span>Files<span class="token punctuation">)</span>
        <span class="token keyword">if</span> dialog<span class="token punctuation">.</span><span class="token keyword">exec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
            filename <span class="token operator">=</span> dialog<span class="token punctuation">.</span>selectedFiles<span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Path<span class="token punctuation">(</span>filename<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token boolean">False</span>

    <span class="token keyword">def</span> <span class="token function">compress</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        f_path <span class="token operator">=</span> self<span class="token punctuation">.</span>fileDialog<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setReadOnly<span class="token punctuation">(</span><span class="token boolean">True</span><span class="token punctuation">)</span>
        s <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"开始压缩"</span><span class="token punctuation">]</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
        result <span class="token operator">=</span> encode<span class="token punctuation">(</span>f_path<span class="token punctuation">)</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {f_path.stem}{f_path.suffix}, byte size: {result[0]}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Compressed file:  byte size: {result[1]}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"HuffCoder file: , byte size: {result[2]}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"Compression rate {result[1]}/{result[0]}={round(result[1]/result[0], 4)*100}%"</span> <span class="token operator">+</span> \
                f<span class="token string">", saved {result[0] - result[1]} bytes"</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">*</span>s<span class="token punctuation">,</span> summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">,</span> <span class="token string">"压缩结束"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit_2<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>result<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__str__<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">decompress</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        f_path <span class="token operator">=</span> self<span class="token punctuation">.</span>fileDialog<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> f_path<span class="token punctuation">.</span>suffix <span class="token operator">!=</span> <span class="token string">".huf"</span><span class="token punctuation">:</span>
            QMessageBox<span class="token punctuation">.</span>warning<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">"警告"</span><span class="token punctuation">,</span> <span class="token string">"请选择正确的压缩文件路径"</span><span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token boolean">False</span>

        <span class="token keyword">if</span> len<span class="token punctuation">(</span>ps <span class="token punctuation">:</span><span class="token operator">=</span> list<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>f_path<span class="token punctuation">)</span><span class="token punctuation">.</span>parent<span class="token punctuation">.</span>glob<span class="token punctuation">(</span><span class="token string">"*.huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">:</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>ps<span class="token punctuation">)</span>
            <span class="token keyword">for</span> p_idx <span class="token keyword">in</span> range<span class="token punctuation">(</span>len<span class="token punctuation">(</span>ps<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                <span class="token keyword">if</span> <span class="token string">"coder"</span> <span class="token keyword">in</span> ps<span class="token punctuation">[</span>p_idx<span class="token punctuation">]</span><span class="token punctuation">.</span>stem<span class="token punctuation">:</span>
                    cp_idx <span class="token operator">=</span> p_idx
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            QMessageBox<span class="token punctuation">.</span>warning<span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token string">"警告"</span><span class="token punctuation">,</span> <span class="token string">"缺少必要的解压缩文件"</span><span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">,</span> QMessageBox<span class="token punctuation">.</span>Yes<span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token boolean">False</span>
        s <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"开始解压"</span><span class="token punctuation">]</span>
        <span class="token keyword">import</span> time
        time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">)</span>
        result <span class="token operator">=</span> decode<span class="token punctuation">(</span>huf_path<span class="token punctuation">:</span><span class="token operator">=</span>ps<span class="token punctuation">[</span><span class="token number">1</span><span class="token operator">-</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token punctuation">:</span><span class="token operator">=</span>ps<span class="token punctuation">[</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">)</span>
        summary1 <span class="token operator">=</span> f<span class="token string">"Origin file: {huf_path.stem}{huf_path.suffix}, byte size: {result[0]}"</span>
        summary2 <span class="token operator">=</span> f<span class="token string">"Coder file: {coder_path.stem}{coder_path.suffix}, byte size: {result[1]}"</span>
        summary3 <span class="token operator">=</span> f<span class="token string">"Decompressed file: byte size: {result[2]}"</span>
        summary4 <span class="token operator">=</span> f<span class="token string">"Decompression rate {result[2]}/{result[0]}={round(result[2]/result[0], 4)*100}%"</span> <span class="token operator">+</span>\
                f<span class="token string">", lost {result[2] - result[0]} bytes"</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setText<span class="token punctuation">(</span><span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">*</span>s<span class="token punctuation">,</span> summary1<span class="token punctuation">,</span> summary2<span class="token punctuation">,</span> summary3<span class="token punctuation">,</span> summary4<span class="token punctuation">,</span> <span class="token string">"解压结束"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>ui<span class="token punctuation">.</span>textEdit_2<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>result<span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">.</span>__str__<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>



<span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span>args<span class="token punctuation">:</span> argparse<span class="token punctuation">.</span>Namespace<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> args<span class="token punctuation">.</span>gui<span class="token punctuation">:</span>
        <span class="token keyword">assert</span> <span class="token number">0</span> <span class="token operator">&lt;</span> int<span class="token punctuation">(</span>args<span class="token punctuation">.</span>src_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">)</span> <span class="token operator">+</span> int<span class="token punctuation">(</span>args<span class="token punctuation">.</span>target_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">)</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}请选择压缩或者解压，使用-h查询用法{Style.RESET_ALL}"</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> args<span class="token punctuation">.</span>gui<span class="token punctuation">:</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"{Fore.GREEN}命令行工作模式{Style.RESET_ALL}"</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> args<span class="token punctuation">.</span>src_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"开始压缩 {args.src_path}"</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> args<span class="token punctuation">.</span>v<span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>encode<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>args<span class="token punctuation">.</span>src_path<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                encode<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>args<span class="token punctuation">.</span>src_path<span class="token punctuation">)</span><span class="token punctuation">,</span> verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"压缩完成"</span><span class="token punctuation">)</span>
        <span class="token keyword">elif</span> args<span class="token punctuation">.</span>target_path <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">if</span> len<span class="token punctuation">(</span>ps <span class="token punctuation">:</span><span class="token operator">=</span> list<span class="token punctuation">(</span>Path<span class="token punctuation">(</span>args<span class="token punctuation">.</span>target_path<span class="token punctuation">)</span><span class="token punctuation">.</span>glob<span class="token punctuation">(</span><span class="token string">"*.huf"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>ps<span class="token punctuation">)</span>
                <span class="token keyword">for</span> p_idx <span class="token keyword">in</span> range<span class="token punctuation">(</span>len<span class="token punctuation">(</span>ps<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
                    <span class="token keyword">if</span> <span class="token string">"coder"</span> <span class="token keyword">in</span> ps<span class="token punctuation">[</span>p_idx<span class="token punctuation">]</span><span class="token punctuation">.</span>stem<span class="token punctuation">:</span>
                        cp_idx <span class="token operator">=</span> p_idx
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                <span class="token keyword">assert</span> <span class="token boolean">False</span><span class="token punctuation">,</span> f<span class="token string">"{Fore.RED}无效的路径，没有找到压缩文件{Style.RESET_ALL}"</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span>f<span class="token string">"开始解压 {args.target_path}"</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> args<span class="token punctuation">.</span>v<span class="token punctuation">:</span>
                <span class="token keyword">print</span><span class="token punctuation">(</span>decode<span class="token punctuation">(</span>huf_path<span class="token operator">=</span>ps<span class="token punctuation">[</span><span class="token number">1</span><span class="token operator">-</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token operator">=</span>ps<span class="token punctuation">[</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>print_code_table<span class="token punctuation">(</span>verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                decode<span class="token punctuation">(</span>huf_path<span class="token operator">=</span>ps<span class="token punctuation">[</span><span class="token number">1</span><span class="token operator">-</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> coder_path<span class="token operator">=</span>ps<span class="token punctuation">[</span>cp_idx<span class="token punctuation">]</span><span class="token punctuation">,</span> verbose<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
            <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"解压完成"</span><span class="token punctuation">)</span>
    <span class="token keyword">else</span><span class="token punctuation">:</span>
        app <span class="token operator">=</span> QApplication<span class="token punctuation">(</span>sys<span class="token punctuation">.</span>argv<span class="token punctuation">)</span>
        m <span class="token operator">=</span> QtGUIHuff<span class="token punctuation">(</span><span class="token punctuation">)</span>
        m<span class="token punctuation">.</span>show<span class="token punctuation">(</span><span class="token punctuation">)</span>
        sys<span class="token punctuation">.</span>exit<span class="token punctuation">(</span>app<span class="token punctuation">.</span>exec_<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>




<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>
    main<span class="token punctuation">(</span>parse_args<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<h3 id="2-Ui-main-py"><a href="#2-Ui-main-py" class="headerlink" title="2. Ui_main.py"></a>2. Ui_main.py</h3><pre class="line-numbers language-python"><code class="language-python"><span class="token comment" spellcheck="true"># -*- coding: utf-8 -*-</span>

<span class="token comment" spellcheck="true"># Form implementation generated from reading ui file '/media/jack/JackCode/project/data_structure/11_huffman/ui/main.ui'</span>
<span class="token comment" spellcheck="true">#</span>
<span class="token comment" spellcheck="true"># Created by: PyQt5 UI code generator 5.9.2</span>
<span class="token comment" spellcheck="true">#</span>
<span class="token comment" spellcheck="true"># WARNING! All changes made in this file will be lost!</span>

<span class="token keyword">from</span> PyQt5 <span class="token keyword">import</span> QtCore<span class="token punctuation">,</span> QtGui<span class="token punctuation">,</span> QtWidgets

<span class="token keyword">class</span> <span class="token class-name">Ui_Form</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">setupUi</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> Form<span class="token punctuation">)</span><span class="token punctuation">:</span>
        Form<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"Form"</span><span class="token punctuation">)</span>
        Form<span class="token punctuation">.</span>resize<span class="token punctuation">(</span><span class="token number">751</span><span class="token punctuation">,</span> <span class="token number">374</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>layoutWidget <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QWidget<span class="token punctuation">(</span>Form<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">.</span>setGeometry<span class="token punctuation">(</span>QtCore<span class="token punctuation">.</span>QRect<span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">641</span><span class="token punctuation">,</span> <span class="token number">321</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"layoutWidget"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout_2 <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QHBoxLayout<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout_2<span class="token punctuation">.</span>setContentsMargins<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout_2<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"horizontalLayout_2"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QVBoxLayout<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"verticalLayout"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QHBoxLayout<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"horizontalLayout"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>compress <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QPushButton<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>compress<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"compress"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout<span class="token punctuation">.</span>addWidget<span class="token punctuation">(</span>self<span class="token punctuation">.</span>compress<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>decompress <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QPushButton<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>decompress<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"decompress"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout<span class="token punctuation">.</span>addWidget<span class="token punctuation">(</span>self<span class="token punctuation">.</span>decompress<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout<span class="token punctuation">.</span>addLayout<span class="token punctuation">(</span>self<span class="token punctuation">.</span>horizontalLayout<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QLabel<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label<span class="token punctuation">.</span>setAlignment<span class="token punctuation">(</span>QtCore<span class="token punctuation">.</span>Qt<span class="token punctuation">.</span>AlignCenter<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"label"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout<span class="token punctuation">.</span>addWidget<span class="token punctuation">(</span>self<span class="token punctuation">.</span>label<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>textEdit <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QTextEdit<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>textEdit<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"textEdit"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout<span class="token punctuation">.</span>addWidget<span class="token punctuation">(</span>self<span class="token punctuation">.</span>textEdit<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout_2<span class="token punctuation">.</span>addLayout<span class="token punctuation">(</span>self<span class="token punctuation">.</span>verticalLayout<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout_2 <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QVBoxLayout<span class="token punctuation">(</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout_2<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"verticalLayout_2"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label_2 <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QLabel<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label_2<span class="token punctuation">.</span>setAlignment<span class="token punctuation">(</span>QtCore<span class="token punctuation">.</span>Qt<span class="token punctuation">.</span>AlignCenter<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label_2<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"label_2"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout_2<span class="token punctuation">.</span>addWidget<span class="token punctuation">(</span>self<span class="token punctuation">.</span>label_2<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>textEdit_2 <span class="token operator">=</span> QtWidgets<span class="token punctuation">.</span>QTextEdit<span class="token punctuation">(</span>self<span class="token punctuation">.</span>layoutWidget<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>textEdit_2<span class="token punctuation">.</span>setObjectName<span class="token punctuation">(</span><span class="token string">"textEdit_2"</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>verticalLayout_2<span class="token punctuation">.</span>addWidget<span class="token punctuation">(</span>self<span class="token punctuation">.</span>textEdit_2<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>horizontalLayout_2<span class="token punctuation">.</span>addLayout<span class="token punctuation">(</span>self<span class="token punctuation">.</span>verticalLayout_2<span class="token punctuation">)</span>

        self<span class="token punctuation">.</span>retranslateUi<span class="token punctuation">(</span>Form<span class="token punctuation">)</span>
        QtCore<span class="token punctuation">.</span>QMetaObject<span class="token punctuation">.</span>connectSlotsByName<span class="token punctuation">(</span>Form<span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">retranslateUi</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> Form<span class="token punctuation">)</span><span class="token punctuation">:</span>
        _translate <span class="token operator">=</span> QtCore<span class="token punctuation">.</span>QCoreApplication<span class="token punctuation">.</span>translate
        Form<span class="token punctuation">.</span>setWindowTitle<span class="token punctuation">(</span>_translate<span class="token punctuation">(</span><span class="token string">"Form"</span><span class="token punctuation">,</span> <span class="token string">"Form"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>compress<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>_translate<span class="token punctuation">(</span><span class="token string">"Form"</span><span class="token punctuation">,</span> <span class="token string">"压缩"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>decompress<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>_translate<span class="token punctuation">(</span><span class="token string">"Form"</span><span class="token punctuation">,</span> <span class="token string">"解压"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>_translate<span class="token punctuation">(</span><span class="token string">"Form"</span><span class="token punctuation">,</span> <span class="token string">"Compress/Decompress Log"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>label_2<span class="token punctuation">.</span>setText<span class="token punctuation">(</span>_translate<span class="token punctuation">(</span><span class="token string">"Form"</span><span class="token punctuation">,</span> <span class="token string">"Huffman Tree"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">Jack Wang</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://jackwang0107.github.io/2021/12/21/ha-fu-man-ya-suo-suan-fa-python-shi-xian/">https://jackwang0107.github.io/2021/12/21/ha-fu-man-ya-suo-suan-fa-python-shi-xian/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/about" target="_blank">Jack Wang</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/DataStructure/">
                                    <span class="chip bg-color">DataStructure</span>
                                </a>
                            
                                <a href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">
                                    <span class="chip bg-color">数据结构</span>
                                </a>
                            
                                <a href="/tags/Python/">
                                    <span class="chip bg-color">Python</span>
                                </a>
                            
                                <a href="/tags/GUI/">
                                    <span class="chip bg-color">GUI</span>
                                </a>
                            
                                <a href="/tags/PyQt/">
                                    <span class="chip bg-color">PyQt</span>
                                </a>
                            
                                <a href="/tags/HuffmanEncode/">
                                    <span class="chip bg-color">HuffmanEncode</span>
                                </a>
                            
                                <a href="/tags/HuffmanAlgorithm/">
                                    <span class="chip bg-color">HuffmanAlgorithm</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.png" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.jpg" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/2021/12/22/a-suan-fa-python-shi-xian/">
                    <div class="card-image">
                        
                        <img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/a_star.gif" class="responsive-img" alt="A*算法-Python实现">
                        
                        <span class="card-title">A*算法-Python实现</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            本文主要提供了Python版本的A*路径规划算法实现，并在此基础上提供了命令行和基于Matplotlib的GUI用户界面（User Interface）
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2021-12-22
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/" class="post-category">
                                    数据结构与算法
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/DataStructure/">
                        <span class="chip bg-color">DataStructure</span>
                    </a>
                    
                    <a href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">
                        <span class="chip bg-color">数据结构</span>
                    </a>
                    
                    <a href="/tags/A/">
                        <span class="chip bg-color">A*</span>
                    </a>
                    
                    <a href="/tags/PathPlanning/">
                        <span class="chip bg-color">PathPlanning</span>
                    </a>
                    
                    <a href="/tags/Python/">
                        <span class="chip bg-color">Python</span>
                    </a>
                    
                    <a href="/tags/Matplotlib/">
                        <span class="chip bg-color">Matplotlib</span>
                    </a>
                    
                    <a href="/tags/GUI/">
                        <span class="chip bg-color">GUI</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2021/12/20/pyqt5-xue-xi-bi-ji-3-di-yi-ge-qt-cheng-xu/">
                    <div class="card-image">
                        
                        <img src="https://jack-1307599355.cos.ap-shanghai.myqcloud.com/img/image-20211220145356314.png" class="responsive-img" alt="PyQt5学习笔记 Chapter 0-Introduction： Part 3.第一个Qt程序">
                        
                        <span class="card-title">PyQt5学习笔记 Chapter 0-Introduction： Part 3.第一个Qt程序</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            本讲作为Introduction的最后一讲，实现了一个简单的Qt的GUI程序
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2021-12-20
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/PyQt5-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/" class="post-category">
                                    PyQt5 学习笔记
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Python/">
                        <span class="chip bg-color">Python</span>
                    </a>
                    
                    <a href="/tags/GUI%E5%BC%80%E5%8F%91/">
                        <span class="chip bg-color">GUI开发</span>
                    </a>
                    
                    <a href="/tags/PyQt/">
                        <span class="chip bg-color">PyQt</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>


<script>
    $('#articleContent').on('copy', function (e) {
        // IE8 or earlier browser is 'undefined'
        if (typeof window.getSelection === 'undefined') return;

        var selection = window.getSelection();
        // if the selection is short let's not annoy our users.
        if (('' + selection).length < Number.parseInt('120')) {
            return;
        }

        // create a div outside of the visible area and fill it with the selected text.
        var bodyElement = document.getElementsByTagName('body')[0];
        var newdiv = document.createElement('div');
        newdiv.style.position = 'absolute';
        newdiv.style.left = '-99999px';
        bodyElement.appendChild(newdiv);
        newdiv.appendChild(selection.getRangeAt(0).cloneContents());

        // we need a <pre> tag workaround.
        // otherwise the text inside "pre" loses all the line breaks!
        if (selection.getRangeAt(0).commonAncestorContainer.nodeName === 'PRE' || selection.getRangeAt(0).commonAncestorContainer.nodeName === 'CODE') {
            newdiv.innerHTML = "<pre>" + newdiv.innerHTML + "</pre>";
        }

        var url = document.location.href;
        newdiv.innerHTML += '<br />'
            + '来源: JackWang&#39;s Blog<br />'
            + '文章作者: Jack Wang<br />'
            + '文章链接: <a href="' + url + '">' + url + '</a><br />'
            + '本文章著作权归作者所有，任何形式的转载都请注明出处。';

        selection.selectAllChildren(newdiv);
        window.setTimeout(function () {bodyElement.removeChild(newdiv);}, 200);
    });
</script>


<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('2'),
            headingSelector: 'h1, h2, h3, h4, h5, h6'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h1, h2, h3, h4, h5, h6').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    

    <div class="container row center-align"
         style="margin-bottom: 15px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2021-2023</span>
            
            <a href="/about" target="_blank">Jack Wang</a>
            <!-- |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a> -->
            <!-- |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a> -->
            <br>
            
                &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                        class="white-color">603.8k</span>
            
            
            
                
            
            
                <span id="busuanzi_container_site_pv">
                &nbsp;|&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;
                    <span id="busuanzi_value_site_pv" class="white-color"></span>
            </span>
            
            
                <span id="busuanzi_container_site_uv">
                &nbsp;|&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;
                    <span id="busuanzi_value_site_uv" class="white-color"></span>
            </span>
            
            <br>

            <!-- 运行天数提醒. -->
            
                <span id="sitetime"> Loading ...</span>
                <script>
                    var calcSiteTime = function () {
                        var seconds = 1000;
                        var minutes = seconds * 60;
                        var hours = minutes * 60;
                        var days = hours * 24;
                        var years = days * 365;
                        var today = new Date();
                        var startYear = "2021";
                        var startMonth = "11";
                        var startDate = "12";
                        var startHour = "0";
                        var startMinute = "0";
                        var startSecond = "0";
                        var todayYear = today.getFullYear();
                        var todayMonth = today.getMonth() + 1;
                        var todayDate = today.getDate();
                        var todayHour = today.getHours();
                        var todayMinute = today.getMinutes();
                        var todaySecond = today.getSeconds();
                        var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                        var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                        var diff = t2 - t1;
                        var diffYears = Math.floor(diff / years);
                        var diffDays = Math.floor((diff / days) - diffYears * 365);

                        // 区分是否有年份.
                        var language = 'zh-CN';
                        if (startYear === String(todayYear)) {
                            document.getElementById("year").innerHTML = todayYear;
                            var daysTip = 'This site has been running for ' + diffDays + ' days';
                            if (language === 'zh-CN') {
                                daysTip = '本站已运行 ' + diffDays + ' 天';
                            } else if (language === 'zh-HK') {
                                daysTip = '本站已運行 ' + diffDays + ' 天';
                            }
                            document.getElementById("sitetime").innerHTML = daysTip;
                        } else {
                            document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                            var yearsAndDaysTip = 'This site has been running for ' + diffYears + ' years and '
                                + diffDays + ' days';
                            if (language === 'zh-CN') {
                                yearsAndDaysTip = '本站已运行 ' + diffYears + ' 年 ' + diffDays + ' 天';
                            } else if (language === 'zh-HK') {
                                yearsAndDaysTip = '本站已運行 ' + diffYears + ' 年 ' + diffDays + ' 天';
                            }
                            document.getElementById("sitetime").innerHTML = yearsAndDaysTip;
                        }
                    }

                    calcSiteTime();
                </script>
            
            <br>
            
                <span id="icp"><img src="/medias/icp.png"
                                    style="vertical-align: text-bottom;"/>
                <a href="https://beian.miit.gov.cn" target="_blank">陕ICP备2021014294号-1</a>
            </span>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/jackwang0108" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:2232123545@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=2232123545" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 2232123545" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    

    
        
        <script type="text/javascript">
            // 只在桌面版网页启用特效
            var windowWidth = $(window).width();
            if (windowWidth > 768) {
                document.write('<script type="text/javascript" src="/libs/others/sakura.js"><\/script>');
            }
        </script>
    

    <!-- 雪花特效 -->
    

    <!-- 鼠标星星特效 -->
    

     
        <script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
        <script src="/libs/others/TencentCaptcha.js"></script>
        <button id="TencentCaptcha" data-appid="xxxxxxxxxx" data-cbfn="callback" type="button" hidden></button>
    

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    <!--腾讯兔小巢-->
    
    

    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
